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.
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
+