capybara-webkit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|