capybara-webkit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +55 -0
  5. data/LICENSE +19 -0
  6. data/README.md +37 -0
  7. data/Rakefile +76 -0
  8. data/capybara-webkit.gemspec +15 -0
  9. data/extconf.rb +2 -0
  10. data/lib/capybara-webkit.rb +7 -0
  11. data/lib/capybara/driver/webkit.rb +84 -0
  12. data/lib/capybara/driver/webkit/browser.rb +86 -0
  13. data/lib/capybara/driver/webkit/node.rb +87 -0
  14. data/spec/driver_spec.rb +465 -0
  15. data/spec/integration/driver_spec.rb +21 -0
  16. data/spec/integration/session_spec.rb +12 -0
  17. data/spec/spec_helper.rb +22 -0
  18. data/spec/support/socket_debugger.rb +42 -0
  19. data/src/Command.cpp +15 -0
  20. data/src/Command.h +28 -0
  21. data/src/Connection.cpp +130 -0
  22. data/src/Connection.h +36 -0
  23. data/src/Evaluate.cpp +84 -0
  24. data/src/Evaluate.h +22 -0
  25. data/src/Execute.cpp +17 -0
  26. data/src/Execute.h +12 -0
  27. data/src/Find.cpp +20 -0
  28. data/src/Find.h +13 -0
  29. data/src/JavascriptInvocation.cpp +14 -0
  30. data/src/JavascriptInvocation.h +19 -0
  31. data/src/Node.cpp +14 -0
  32. data/src/Node.h +13 -0
  33. data/src/Reset.cpp +16 -0
  34. data/src/Reset.h +12 -0
  35. data/src/Server.cpp +21 -0
  36. data/src/Server.h +20 -0
  37. data/src/Source.cpp +14 -0
  38. data/src/Source.h +12 -0
  39. data/src/Url.cpp +14 -0
  40. data/src/Url.h +12 -0
  41. data/src/Visit.cpp +20 -0
  42. data/src/Visit.h +16 -0
  43. data/src/WebPage.cpp +81 -0
  44. data/src/WebPage.h +29 -0
  45. data/src/capybara.js +98 -0
  46. data/src/find_command.h +13 -0
  47. data/src/main.cpp +20 -0
  48. data/src/webkit_server.pro +9 -0
  49. data/src/webkit_server.qrc +5 -0
  50. data/templates/Command.cpp +10 -0
  51. data/templates/Command.h +12 -0
  52. data/webkit_server.pro +4 -0
  53. 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
@@ -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
+
@@ -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
+