Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge remote-tracking branch 'origin/master'

Conflicts:
	lib/capybara/driver/webkit/browser.rb
  • Loading branch information...
commit bd4d9e5ba2f822f22115b3f03169aa43f98a0730 2 parents e6143df + 554c018
@rayners authored
View
2  README.md
@@ -83,7 +83,7 @@ capybara-webkit supports a few methods that are not part of the standard capybar
page.driver.evaluate_script("window.innerWidth")
=> 500
-**render**: render a screenshot of the current view
+**render**: render a screenshot of the current view (requires [mini_magick](https://github.com/probablycorey/mini_magick) and [ImageMagick](http://www.imagemagick.org))
page.driver.render "tmp/screenshot.png"
View
5 lib/capybara/driver/webkit.rb
@@ -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,8 +23,8 @@ def initialize(app, options={})
@options = options
@rack_server = Capybara::Server.new(@app)
@rack_server.boot if Capybara.run_server
- @browser = options[:browser] || Browser.new(
- :ignore_ssl_errors => options[:ignore_ssl_errors])
+ @browser = options[:browser] || Browser.new(Connection.new(options))
+ @browser.ignore_ssl_errors if options[:ignore_ssl_errors]
end
def current_url
View
124 lib/capybara/driver/webkit/browser.rb
@@ -1,20 +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
- @ignore_ssl_errors = options[:ignore_ssl_errors]
- start_server
- connect
+ def initialize(connection)
+ @connection = connection
end
def visit(url)
@@ -84,6 +73,10 @@ def frame_focus(frame_id_or_index=nil)
end
end
+ def ignore_ssl_errors
+ command("IgnoreSslErrors")
+ end
+
def command(name, *args)
retry_command(0, name, *args)
end
@@ -128,91 +121,8 @@ def resize_window(width, height)
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]
- cmdline << "--ignore-ssl-errors" if @ignore_ssl_errors
- 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?
@@ -225,10 +135,14 @@ def check
end
def read_response
- response_length = @socket.gets.to_i
- response = @socket.read(response_length)
- response.force_encoding("UTF-8") if response.respond_to?(:force_encoding)
- response
+ response_length = @connection.gets.to_i
+ if response_length > 0
+ response = @connection.read(response_length)
+ response.force_encoding("UTF-8") if response.respond_to?(:force_encoding)
+ response
+ else
+ ""
+ end
end
def default_proxy_options
@@ -241,11 +155,11 @@ def default_proxy_options
end
def retry_command(count, 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
View
120 lib/capybara/driver/webkit/connection.rb
@@ -0,0 +1,120 @@
+require 'socket'
+require 'timeout'
+require 'thread'
+
+class Capybara::Driver::Webkit
+ class Connection
+ WEBKIT_SERVER_START_TIMEOUT = 15
+
+ 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, WEBKIT_SERVER_START_TIMEOUT)
+ ((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
View
45 spec/browser_spec.rb
@@ -2,42 +2,19 @@
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(:ignore_ssl_errors => true)
- }
-
- describe '#server_port' do
- subject { browser.server_port }
- it 'returns a valid port number' do
- should be_a(Integer)
- 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
+ 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
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
before do
# set up minimal HTTPS server
@@ -181,4 +158,16 @@
@proxy_requests.size.should == 0
end
end
+
+ it "doesn't try to read an empty response" do
+ connection = stub("connection")
+ connection.stub(:puts)
+ connection.stub(:print)
+ connection.stub(:gets).and_return("ok\n", "0\n")
+ connection.stub(:read).and_raise(StandardError.new("tried to read empty response"))
+
+ browser = Capybara::Driver::Webkit::Browser.new(connection)
+
+ expect { browser.visit("/") }.not_to raise_error(/empty response/)
+ end
end
View
54 spec/connection_spec.rb
@@ -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
View
48 spec/driver_spec.rb
@@ -988,15 +988,15 @@ def wait_for_error_to_complete
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 @@ def echoed_cookie
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) }
@@ -1277,6 +1278,39 @@ def set_automatic_reload(value)
end
end
+ context "localStorage works" do
+ before(:all) do
+ @app = lambda do |env|
+ body = <<-HTML
+ <html>
+ <body>
+ <span id='output'></span>
+ <script type="text/javascript">
+ if (typeof localStorage !== "undefined") {
+ if (!localStorage.refreshCounter) {
+ localStorage.refreshCounter = 0;
+ }
+ if (localStorage.refreshCounter++ > 0) {
+ document.getElementById("output").innerHTML = "localStorage is enabled";
+ }
+ }
+ </script>
+ </body>
+ </html>
+ HTML
+ [200,
+ { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
+ [body]]
+ end
+ end
+
+ it "displays the message on subsequent page loads" do
+ subject.find("//span[contains(.,'localStorage is enabled')]").should be_empty
+ subject.visit "/"
+ subject.find("//span[contains(.,'localStorage is enabled')]").should_not be_empty
+ end
+ end
+
context "app with a lot of HTML tags" do
before(:all) do
@app = lambda do |env|
View
4 spec/spec_helper.rb
@@ -22,8 +22,10 @@
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)
View
1  src/CommandFactory.cpp
@@ -23,6 +23,7 @@
#include "RequestedUrl.h"
#include "CurrentUrl.h"
#include "ResizeWindow.h"
+#include "IgnoreSslErrors.h"
CommandFactory::CommandFactory(WebPage *page, QObject *parent) : QObject(parent) {
m_page = page;
View
12 src/IgnoreSslErrors.cpp
@@ -0,0 +1,12 @@
+#include "IgnoreSslErrors.h"
+#include "WebPage.h"
+
+IgnoreSslErrors::IgnoreSslErrors(WebPage *page, QStringList &arguments, QObject *parent) :
+ Command(page, arguments, parent) {
+}
+
+void IgnoreSslErrors::start() {
+ page()->ignoreSslErrors();
+ emit finished(new Response(true));
+}
+
View
12 src/IgnoreSslErrors.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+
+class WebPage;
+
+class IgnoreSslErrors : public Command {
+ Q_OBJECT
+
+ public:
+ IgnoreSslErrors(WebPage *page, QStringList &arguments, QObject *parent = 0);
+ virtual void start();
+};
+
View
3  src/Server.cpp
@@ -4,10 +4,9 @@
#include <QTcpServer>
-Server::Server(QObject *parent, bool ignoreSslErrors) : QObject(parent) {
+Server::Server(QObject *parent) : QObject(parent) {
m_tcp_server = new QTcpServer(this);
m_page = new WebPage(this);
- m_page->setIgnoreSslErrors(ignoreSslErrors);
}
bool Server::start() {
View
2  src/Server.h
@@ -7,7 +7,7 @@ class Server : public QObject {
Q_OBJECT
public:
- Server(QObject *parent, bool ignoreSslErrors);
+ Server(QObject *parent);
bool start();
quint16 server_port() const;
View
16 src/WebPage.cpp
@@ -12,6 +12,7 @@ WebPage::WebPage(QObject *parent) : QWebPage(parent) {
setUserStylesheet();
m_loading = false;
+ m_ignoreSslErrors = false;
this->setCustomNetworkAccessManager();
connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
@@ -25,6 +26,7 @@ WebPage::WebPage(QObject *parent) : QWebPage(parent) {
void WebPage::resetWindowSize() {
this->setViewportSize(QSize(1680, 1050));
+ this->settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
}
void WebPage::setCustomNetworkAccessManager() {
@@ -32,7 +34,8 @@ void WebPage::setCustomNetworkAccessManager() {
manager->setCookieJar(new NetworkCookieJar());
this->setNetworkAccessManager(manager);
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
- connect(manager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>)));
+ connect(manager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)),
+ this, SLOT(handleSslErrorsForReply(QNetworkReply *, QList<QSslError>)));
}
void WebPage::loadJavascript() {
@@ -204,20 +207,15 @@ void WebPage::replyFinished(QNetworkReply *reply) {
}
}
-void WebPage::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors) {
+void WebPage::handleSslErrorsForReply(QNetworkReply *reply, const QList<QSslError> &errors) {
if (m_ignoreSslErrors)
reply->ignoreSslErrors(errors);
}
-void WebPage::setIgnoreSslErrors(bool ignore) {
- m_ignoreSslErrors = ignore;
+void WebPage::ignoreSslErrors() {
+ m_ignoreSslErrors = true;
}
-bool WebPage::ignoreSslErrors() {
- return m_ignoreSslErrors;
-}
-
-
int WebPage::getLastStatus() {
return m_lastStatus;
}
View
5 src/WebPage.h
@@ -15,8 +15,7 @@ class WebPage : public QWebPage {
void setCustomNetworkAccessManager();
bool render(const QString &fileName);
virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
- void setIgnoreSslErrors(bool ignore);
- bool ignoreSslErrors();
+ void ignoreSslErrors();
QString consoleMessages();
void resetConsoleMessages();
void resetWindowSize();
@@ -30,7 +29,7 @@ class WebPage : public QWebPage {
QString pageHeaders();
void frameCreated(QWebFrame *);
void replyFinished(QNetworkReply *reply);
- void ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &);
+ void handleSslErrorsForReply(QNetworkReply *reply, const QList<QSslError> &);
void handleUnsupportedContent(QNetworkReply *reply);
signals:
View
1  src/find_command.h
@@ -26,3 +26,4 @@ CHECK_COMMAND(ConsoleMessages)
CHECK_COMMAND(RequestedUrl)
CHECK_COMMAND(CurrentUrl)
CHECK_COMMAND(ResizeWindow)
+CHECK_COMMAND(IgnoreSslErrors)
View
3  src/main.cpp
@@ -19,9 +19,8 @@ int main(int argc, char **argv) {
app.setOrganizationDomain("thoughtbot.com");
QStringList args = app.arguments();
- bool ignoreSslErrors = args.contains("--ignore-ssl-errors");
- Server server(0, ignoreSslErrors);
+ Server server(0);
if (server.start()) {
std::cout << "Capybara-webkit server started, listening on port: " << server.server_port() << std::endl;
View
2  src/webkit_server.pro
@@ -2,6 +2,7 @@ TEMPLATE = app
TARGET = webkit_server
DESTDIR = .
HEADERS = \
+ IgnoreSslErrors.h \
ResizeWindow.h \
CurrentUrl.h \
RequestedUrl.h \
@@ -39,6 +40,7 @@ HEADERS = \
PageLoadingCommand.h \
SOURCES = \
+ IgnoreSslErrors.cpp \
ResizeWindow.cpp \
CurrentUrl.cpp \
RequestedUrl.cpp \
Please sign in to comment.
Something went wrong with that request. Please try again.