capybara-webkit 0.1.0
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.
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +55 -0
- data/LICENSE +19 -0
- data/README.md +37 -0
- data/Rakefile +76 -0
- data/capybara-webkit.gemspec +15 -0
- data/extconf.rb +2 -0
- data/lib/capybara-webkit.rb +7 -0
- data/lib/capybara/driver/webkit.rb +84 -0
- data/lib/capybara/driver/webkit/browser.rb +86 -0
- data/lib/capybara/driver/webkit/node.rb +87 -0
- data/spec/driver_spec.rb +465 -0
- data/spec/integration/driver_spec.rb +21 -0
- data/spec/integration/session_spec.rb +12 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/socket_debugger.rb +42 -0
- data/src/Command.cpp +15 -0
- data/src/Command.h +28 -0
- data/src/Connection.cpp +130 -0
- data/src/Connection.h +36 -0
- data/src/Evaluate.cpp +84 -0
- data/src/Evaluate.h +22 -0
- data/src/Execute.cpp +17 -0
- data/src/Execute.h +12 -0
- data/src/Find.cpp +20 -0
- data/src/Find.h +13 -0
- data/src/JavascriptInvocation.cpp +14 -0
- data/src/JavascriptInvocation.h +19 -0
- data/src/Node.cpp +14 -0
- data/src/Node.h +13 -0
- data/src/Reset.cpp +16 -0
- data/src/Reset.h +12 -0
- data/src/Server.cpp +21 -0
- data/src/Server.h +20 -0
- data/src/Source.cpp +14 -0
- data/src/Source.h +12 -0
- data/src/Url.cpp +14 -0
- data/src/Url.h +12 -0
- data/src/Visit.cpp +20 -0
- data/src/Visit.h +16 -0
- data/src/WebPage.cpp +81 -0
- data/src/WebPage.h +29 -0
- data/src/capybara.js +98 -0
- data/src/find_command.h +13 -0
- data/src/main.cpp +20 -0
- data/src/webkit_server.pro +9 -0
- data/src/webkit_server.qrc +5 -0
- data/templates/Command.cpp +10 -0
- data/templates/Command.h +12 -0
- data/webkit_server.pro +4 -0
- metadata +140 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'capybara/driver/webkit'
|
3
|
+
|
4
|
+
describe Capybara::Driver::Webkit do
|
5
|
+
before do
|
6
|
+
@driver = Capybara::Driver::Webkit.new(TestApp, :browser => $webkit_browser)
|
7
|
+
end
|
8
|
+
|
9
|
+
# TODO: select options
|
10
|
+
# it_should_behave_like "driver"
|
11
|
+
|
12
|
+
# TODO: bug with drag and drop
|
13
|
+
# it_should_behave_like "driver with javascript support"
|
14
|
+
|
15
|
+
it_should_behave_like "driver with cookies support"
|
16
|
+
|
17
|
+
# Can't support:
|
18
|
+
# it_should_behave_like "driver with header support"
|
19
|
+
# it_should_behave_like "driver with status code support"
|
20
|
+
# it_should_behave_like "driver with frame support"
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'capybara-webkit'
|
3
|
+
|
4
|
+
describe Capybara::Session do
|
5
|
+
before do
|
6
|
+
@session = Capybara::Session.new(:webkit, TestApp)
|
7
|
+
end
|
8
|
+
|
9
|
+
# TODO: needs to only use one Browser throughout tests
|
10
|
+
# it_should_behave_like "session"
|
11
|
+
# it_should_behave_like "session with javascript support"
|
12
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rspec/autorun'
|
3
|
+
|
4
|
+
PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')).freeze
|
5
|
+
|
6
|
+
$LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
|
7
|
+
|
8
|
+
Dir[File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')].each { |file| require(file) }
|
9
|
+
|
10
|
+
spec_dir = nil
|
11
|
+
$:.detect do |dir|
|
12
|
+
if File.exists? File.join(dir, "capybara.rb")
|
13
|
+
spec_dir = File.expand_path(File.join(dir,"..","spec"))
|
14
|
+
$:.unshift( spec_dir )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require File.join(spec_dir,"spec_helper")
|
19
|
+
|
20
|
+
require 'capybara/driver/webkit/browser'
|
21
|
+
$webkit_browser = Capybara::Driver::Webkit::Browser.new(:socket_class => TCPSocket)
|
22
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Wraps the TCP socket and prints data sent and received. Used for debugging
|
2
|
+
# the wire protocol. You can use this by passing a :socket_class to Browser.
|
3
|
+
class SocketDebugger
|
4
|
+
def self.open(host, port)
|
5
|
+
real_socket = TCPSocket.open(host, port)
|
6
|
+
new(real_socket)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(socket)
|
10
|
+
@socket = socket
|
11
|
+
end
|
12
|
+
|
13
|
+
def read(length)
|
14
|
+
received @socket.read(length)
|
15
|
+
end
|
16
|
+
|
17
|
+
def puts(line)
|
18
|
+
sent line
|
19
|
+
@socket.puts(line)
|
20
|
+
end
|
21
|
+
|
22
|
+
def print(content)
|
23
|
+
sent content
|
24
|
+
@socket.print(content)
|
25
|
+
end
|
26
|
+
|
27
|
+
def gets
|
28
|
+
received @socket.gets
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def sent(content)
|
34
|
+
Kernel.puts " >> " + content.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def received(content)
|
38
|
+
Kernel.puts " << " + content.to_s
|
39
|
+
content
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
data/src/Command.cpp
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#include "Command.h"
|
2
|
+
#include "WebPage.h"
|
3
|
+
|
4
|
+
Command::Command(WebPage *page, QObject *parent) : QObject(parent) {
|
5
|
+
m_page = page;
|
6
|
+
}
|
7
|
+
|
8
|
+
void Command::start(QStringList &arguments) {
|
9
|
+
Q_UNUSED(arguments);
|
10
|
+
}
|
11
|
+
|
12
|
+
WebPage *Command::page() {
|
13
|
+
return m_page;
|
14
|
+
}
|
15
|
+
|
data/src/Command.h
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#ifndef COMMAND_H
|
2
|
+
#define COMMAND_H
|
3
|
+
|
4
|
+
#include <QObject>
|
5
|
+
#include <QStringList>
|
6
|
+
|
7
|
+
class WebPage;
|
8
|
+
|
9
|
+
class Command : public QObject {
|
10
|
+
Q_OBJECT
|
11
|
+
|
12
|
+
public:
|
13
|
+
Command(WebPage *page, QObject *parent = 0);
|
14
|
+
virtual void start(QStringList &arguments);
|
15
|
+
|
16
|
+
signals:
|
17
|
+
void finished(bool success, QString &response);
|
18
|
+
|
19
|
+
protected:
|
20
|
+
WebPage *page();
|
21
|
+
|
22
|
+
private:
|
23
|
+
WebPage *m_page;
|
24
|
+
|
25
|
+
};
|
26
|
+
|
27
|
+
#endif
|
28
|
+
|
data/src/Connection.cpp
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
#include "Connection.h"
|
2
|
+
#include "WebPage.h"
|
3
|
+
#include "Visit.h"
|
4
|
+
#include "Find.h"
|
5
|
+
#include "Command.h"
|
6
|
+
#include "Reset.h"
|
7
|
+
#include "Node.h"
|
8
|
+
#include "Url.h"
|
9
|
+
#include "Source.h"
|
10
|
+
#include "Evaluate.h"
|
11
|
+
#include "Execute.h"
|
12
|
+
|
13
|
+
#include <QTcpSocket>
|
14
|
+
#include <iostream>
|
15
|
+
|
16
|
+
Connection::Connection(QTcpSocket *socket, WebPage *page, QObject *parent) :
|
17
|
+
QObject(parent) {
|
18
|
+
m_socket = socket;
|
19
|
+
m_page = page;
|
20
|
+
m_command = NULL;
|
21
|
+
m_expectingDataSize = -1;
|
22
|
+
connect(m_socket, SIGNAL(readyRead()), this, SLOT(checkNext()));
|
23
|
+
}
|
24
|
+
|
25
|
+
void Connection::checkNext() {
|
26
|
+
if (m_expectingDataSize == -1) {
|
27
|
+
if (m_socket->canReadLine()) {
|
28
|
+
readLine();
|
29
|
+
checkNext();
|
30
|
+
}
|
31
|
+
} else {
|
32
|
+
if (m_socket->bytesAvailable() >= m_expectingDataSize) {
|
33
|
+
readDataBlock();
|
34
|
+
checkNext();
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
void Connection::readLine() {
|
40
|
+
char buffer[128];
|
41
|
+
qint64 lineLength = m_socket->readLine(buffer, 128);
|
42
|
+
if (lineLength != -1) {
|
43
|
+
buffer[lineLength - 1] = 0;
|
44
|
+
processNext(buffer);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
void Connection::readDataBlock() {
|
49
|
+
char *buffer = new char[m_expectingDataSize + 1];
|
50
|
+
m_socket->read(buffer, m_expectingDataSize);
|
51
|
+
buffer[m_expectingDataSize] = 0;
|
52
|
+
processNext(buffer);
|
53
|
+
m_expectingDataSize = -1;
|
54
|
+
delete buffer;
|
55
|
+
}
|
56
|
+
|
57
|
+
void Connection::processNext(const char *data) {
|
58
|
+
if (m_commandName.isNull()) {
|
59
|
+
m_commandName = data;
|
60
|
+
m_argumentsExpected = -1;
|
61
|
+
} else {
|
62
|
+
processArgument(data);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
void Connection::processArgument(const char *data) {
|
67
|
+
if (m_argumentsExpected == -1) {
|
68
|
+
m_argumentsExpected = QString(data).toInt();
|
69
|
+
} else if (m_expectingDataSize == -1) {
|
70
|
+
m_expectingDataSize = QString(data).toInt();
|
71
|
+
} else {
|
72
|
+
m_arguments.append(data);
|
73
|
+
}
|
74
|
+
|
75
|
+
if (m_arguments.length() == m_argumentsExpected) {
|
76
|
+
if (m_page->isLoading())
|
77
|
+
connect(m_page, SIGNAL(loadFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
|
78
|
+
else
|
79
|
+
startCommand();
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
void Connection::startCommand() {
|
84
|
+
m_command = createCommand(m_commandName.toAscii().constData());
|
85
|
+
if (m_command) {
|
86
|
+
connect(m_command,
|
87
|
+
SIGNAL(finished(bool, QString &)),
|
88
|
+
this,
|
89
|
+
SLOT(finishCommand(bool, QString &)));
|
90
|
+
m_command->start(m_arguments);
|
91
|
+
} else {
|
92
|
+
QString failure = QString("Unknown command: ") + m_commandName + "\n";
|
93
|
+
writeResponse(false, failure);
|
94
|
+
}
|
95
|
+
m_commandName = QString();
|
96
|
+
}
|
97
|
+
|
98
|
+
Command *Connection::createCommand(const char *name) {
|
99
|
+
#include "find_command.h"
|
100
|
+
return NULL;
|
101
|
+
}
|
102
|
+
|
103
|
+
void Connection::pendingLoadFinished(bool success) {
|
104
|
+
m_page->disconnect(this, SLOT(pendingLoadFinished(bool)));
|
105
|
+
if (success) {
|
106
|
+
startCommand();
|
107
|
+
} else {
|
108
|
+
QString response = m_page->failureString();
|
109
|
+
finishCommand(false, response);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
void Connection::finishCommand(bool success, QString &response) {
|
114
|
+
m_command->deleteLater();
|
115
|
+
m_command = NULL;
|
116
|
+
m_arguments.clear();
|
117
|
+
writeResponse(success, response);
|
118
|
+
}
|
119
|
+
|
120
|
+
void Connection::writeResponse(bool success, QString &response) {
|
121
|
+
if (success)
|
122
|
+
m_socket->write("ok\n");
|
123
|
+
else
|
124
|
+
m_socket->write("failure\n");
|
125
|
+
|
126
|
+
QString responseLength = QString::number(response.size()) + "\n";
|
127
|
+
m_socket->write(responseLength.toAscii());
|
128
|
+
m_socket->write(response.toAscii());
|
129
|
+
}
|
130
|
+
|
data/src/Connection.h
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#include <QObject>
|
2
|
+
#include <QStringList>
|
3
|
+
|
4
|
+
class QTcpSocket;
|
5
|
+
class WebPage;
|
6
|
+
class Command;
|
7
|
+
|
8
|
+
class Connection : public QObject {
|
9
|
+
Q_OBJECT
|
10
|
+
|
11
|
+
public:
|
12
|
+
Connection(QTcpSocket *socket, WebPage *page, QObject *parent = 0);
|
13
|
+
|
14
|
+
public slots:
|
15
|
+
void checkNext();
|
16
|
+
void finishCommand(bool success, QString &response);
|
17
|
+
void pendingLoadFinished(bool success);
|
18
|
+
|
19
|
+
private:
|
20
|
+
void readLine();
|
21
|
+
void readDataBlock();
|
22
|
+
void processNext(const char *line);
|
23
|
+
Command *createCommand(const char *name);
|
24
|
+
void processArgument(const char *line);
|
25
|
+
void startCommand();
|
26
|
+
void writeResponse(bool success, QString &response);
|
27
|
+
|
28
|
+
QTcpSocket *m_socket;
|
29
|
+
QString m_commandName;
|
30
|
+
Command *m_command;
|
31
|
+
QStringList m_arguments;
|
32
|
+
int m_argumentsExpected;
|
33
|
+
WebPage *m_page;
|
34
|
+
int m_expectingDataSize;
|
35
|
+
};
|
36
|
+
|
data/src/Evaluate.cpp
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
#include "Evaluate.h"
|
2
|
+
#include "WebPage.h"
|
3
|
+
#include <iostream>
|
4
|
+
|
5
|
+
Evaluate::Evaluate(WebPage *page, QObject *parent) : Command(page, parent) {
|
6
|
+
m_buffer = "";
|
7
|
+
}
|
8
|
+
|
9
|
+
void Evaluate::start(QStringList &arguments) {
|
10
|
+
QVariant result = page()->mainFrame()->evaluateJavaScript(arguments[0]);
|
11
|
+
addVariant(result);
|
12
|
+
emit finished(true, m_buffer);
|
13
|
+
}
|
14
|
+
|
15
|
+
void Evaluate::addVariant(QVariant &object) {
|
16
|
+
if (object.isValid()) {
|
17
|
+
switch(object.type()) {
|
18
|
+
case QMetaType::QString:
|
19
|
+
{
|
20
|
+
QString string = object.toString();
|
21
|
+
addString(string);
|
22
|
+
}
|
23
|
+
break;
|
24
|
+
case QMetaType::QVariantList:
|
25
|
+
{
|
26
|
+
QVariantList list = object.toList();
|
27
|
+
addArray(list);
|
28
|
+
}
|
29
|
+
break;
|
30
|
+
case QMetaType::Double:
|
31
|
+
m_buffer.append(object.toString());
|
32
|
+
break;
|
33
|
+
case QMetaType::QVariantMap:
|
34
|
+
{
|
35
|
+
QVariantMap map = object.toMap();
|
36
|
+
addMap(map);
|
37
|
+
break;
|
38
|
+
}
|
39
|
+
case QMetaType::Bool:
|
40
|
+
{
|
41
|
+
m_buffer.append(object.toString());
|
42
|
+
break;
|
43
|
+
}
|
44
|
+
default:
|
45
|
+
m_buffer.append("null");
|
46
|
+
}
|
47
|
+
} else {
|
48
|
+
m_buffer.append("null");
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
void Evaluate::addString(QString &string) {
|
53
|
+
QString escapedString(string);
|
54
|
+
escapedString.replace("\"", "\\\"");
|
55
|
+
m_buffer.append("\"");
|
56
|
+
m_buffer.append(escapedString);
|
57
|
+
m_buffer.append("\"");
|
58
|
+
}
|
59
|
+
|
60
|
+
void Evaluate::addArray(QVariantList &list) {
|
61
|
+
m_buffer.append("[");
|
62
|
+
for (int i = 0; i < list.length(); i++) {
|
63
|
+
if (i > 0)
|
64
|
+
m_buffer.append(",");
|
65
|
+
addVariant(list[i]);
|
66
|
+
}
|
67
|
+
m_buffer.append("]");
|
68
|
+
}
|
69
|
+
|
70
|
+
void Evaluate::addMap(QVariantMap &map) {
|
71
|
+
m_buffer.append("{");
|
72
|
+
QMapIterator<QString, QVariant> iterator(map);
|
73
|
+
while (iterator.hasNext()) {
|
74
|
+
iterator.next();
|
75
|
+
QString key = iterator.key();
|
76
|
+
QVariant value = iterator.value();
|
77
|
+
addString(key);
|
78
|
+
m_buffer.append(":");
|
79
|
+
addVariant(value);
|
80
|
+
if (iterator.hasNext())
|
81
|
+
m_buffer.append(",");
|
82
|
+
}
|
83
|
+
m_buffer.append("}");
|
84
|
+
}
|
data/src/Evaluate.h
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#include "Command.h"
|
2
|
+
|
3
|
+
#include <QVariantList>
|
4
|
+
|
5
|
+
class WebPage;
|
6
|
+
|
7
|
+
class Evaluate : public Command {
|
8
|
+
Q_OBJECT
|
9
|
+
|
10
|
+
public:
|
11
|
+
Evaluate(WebPage *page, QObject *parent = 0);
|
12
|
+
virtual void start(QStringList &arguments);
|
13
|
+
|
14
|
+
private:
|
15
|
+
void addVariant(QVariant &object);
|
16
|
+
void addString(QString &string);
|
17
|
+
void addArray(QVariantList &list);
|
18
|
+
void addMap(QVariantMap &map);
|
19
|
+
|
20
|
+
QString m_buffer;
|
21
|
+
};
|
22
|
+
|
data/src/Execute.cpp
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#include "Execute.h"
|
2
|
+
#include "WebPage.h"
|
3
|
+
|
4
|
+
Execute::Execute(WebPage *page, QObject *parent) : Command(page, parent) {
|
5
|
+
}
|
6
|
+
|
7
|
+
void Execute::start(QStringList &arguments) {
|
8
|
+
QVariant result = page()->mainFrame()->evaluateJavaScript(arguments[0]);
|
9
|
+
QString response;
|
10
|
+
if (result.isValid()) {
|
11
|
+
emit finished(true, response);
|
12
|
+
} else {
|
13
|
+
response = "Javascript failed to execute";
|
14
|
+
emit finished(false, response);
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|