capybara-webkit 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -86,6 +86,6 @@ describe Capybara::Session, "with TestApp" do
86
86
  @session = Capybara::Session.new(:reusable_webkit, TestApp)
87
87
  end
88
88
 
89
- # it_should_behave_like "session"
90
- # it_should_behave_like "session with javascript support"
89
+ it_should_behave_like "session"
90
+ it_should_behave_like "session with javascript support"
91
91
  end
data/src/Body.h ADDED
@@ -0,0 +1,12 @@
1
+ #include "Command.h"
2
+
3
+ class WebPage;
4
+
5
+ class Body : public Command {
6
+ Q_OBJECT
7
+
8
+ public:
9
+ Body(WebPage *page, QObject *parent = 0);
10
+ virtual void start(QStringList &arguments);
11
+ };
12
+
data/src/Connection.cpp CHANGED
@@ -10,6 +10,9 @@
10
10
  #include "Evaluate.h"
11
11
  #include "Execute.h"
12
12
  #include "FrameFocus.h"
13
+ #include "Header.h"
14
+ #include "Render.h"
15
+ #include "Body.h"
13
16
 
14
17
  #include <QTcpSocket>
15
18
  #include <iostream>
@@ -55,7 +58,7 @@ void Connection::readDataBlock() {
55
58
  buffer[m_expectingDataSize] = 0;
56
59
  processNext(buffer);
57
60
  m_expectingDataSize = -1;
58
- delete buffer;
61
+ delete[] buffer;
59
62
  }
60
63
 
61
64
  void Connection::processNext(const char *data) {
@@ -95,7 +98,7 @@ void Connection::startCommand() {
95
98
  SLOT(finishCommand(Response *)));
96
99
  m_command->start(m_arguments);
97
100
  } else {
98
- QString failure = QString("Unknown command: ") + m_commandName + "\n";
101
+ QString failure = QString("[Capybara WebKit] Unknown command: ") + m_commandName + "\n";
99
102
  writeResponse(new Response(false, failure));
100
103
  }
101
104
  m_commandName = QString();
data/src/Header.cpp ADDED
@@ -0,0 +1,18 @@
1
+ #include "Header.h"
2
+ #include "WebPage.h"
3
+ #include "NetworkAccessManager.h"
4
+
5
+ Header::Header(WebPage *page, QObject *parent) : Command(page, parent) {
6
+ }
7
+
8
+ void Header::start(QStringList &arguments) {
9
+ QString key = arguments[0];
10
+ QString value = arguments[1];
11
+ NetworkAccessManager* networkAccessManager = qobject_cast<NetworkAccessManager*>(page()->networkAccessManager());
12
+ if (key.toLower().replace("-", "_") == "user_agent") {
13
+ page()->setUserAgent(value);
14
+ } else {
15
+ networkAccessManager->addHeader(key, value);
16
+ }
17
+ emit finished(new Response(true));
18
+ }
data/src/Header.h ADDED
@@ -0,0 +1,11 @@
1
+ #include "Command.h"
2
+
3
+ class WebPage;
4
+
5
+ class Header : public Command {
6
+ Q_OBJECT
7
+
8
+ public:
9
+ Header(WebPage *page, QObject *parent = 0);
10
+ virtual void start(QStringList &arguments);
11
+ };
@@ -0,0 +1,22 @@
1
+ #include "NetworkAccessManager.h"
2
+ #include "WebPage.h"
3
+ #include <iostream>
4
+
5
+
6
+ NetworkAccessManager::NetworkAccessManager(QObject *parent):QNetworkAccessManager(parent) {
7
+ }
8
+
9
+ QNetworkReply* NetworkAccessManager::createRequest(QNetworkAccessManager::Operation oparation, const QNetworkRequest &request, QIODevice * outgoingData = 0) {
10
+ QNetworkRequest new_request(request);
11
+ QHashIterator<QString, QString> item(m_headers);
12
+ while (item.hasNext()) {
13
+ item.next();
14
+ new_request.setRawHeader(item.key().toAscii(), item.value().toAscii());
15
+ }
16
+ return QNetworkAccessManager::createRequest(oparation, new_request, outgoingData);
17
+ };
18
+
19
+ void NetworkAccessManager::addHeader(QString key, QString value) {
20
+ m_headers.insert(key, value);
21
+ };
22
+
@@ -0,0 +1,18 @@
1
+ #include <QtNetwork/QNetworkAccessManager>
2
+ #include <QtNetwork/QNetworkRequest>
3
+ #include <QtNetwork/QNetworkReply>
4
+
5
+ class NetworkAccessManager : public QNetworkAccessManager {
6
+
7
+ Q_OBJECT
8
+
9
+ public:
10
+ NetworkAccessManager(QObject *parent = 0);
11
+ void addHeader(QString key, QString value);
12
+
13
+ protected:
14
+ QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice * outgoingData);
15
+
16
+ private:
17
+ QHash<QString, QString> m_headers;
18
+ };
data/src/Render.cpp ADDED
@@ -0,0 +1,19 @@
1
+ #include "Render.h"
2
+ #include "WebPage.h"
3
+
4
+ Render::Render(WebPage *page, QObject *parent) : Command(page, parent) {
5
+ }
6
+
7
+ void Render::start(QStringList &arguments) {
8
+ QStringList functionArguments(arguments);
9
+ QString imagePath = functionArguments.takeFirst();
10
+ int width = functionArguments.takeFirst().toInt();
11
+ int height = functionArguments.takeFirst().toInt();
12
+
13
+ QSize size(width, height);
14
+ page()->setViewportSize(size);
15
+
16
+ bool result = page()->render( imagePath );
17
+
18
+ emit finished(new Response(result));
19
+ }
data/src/Render.h ADDED
@@ -0,0 +1,12 @@
1
+ #include "Command.h"
2
+ #include <QStringList>
3
+
4
+ class WebPage;
5
+
6
+ class Render : public Command {
7
+ Q_OBJECT
8
+
9
+ public:
10
+ Render(WebPage *page, QObject *parent = 0);
11
+ virtual void start(QStringList &arguments);
12
+ };
data/src/Reset.cpp CHANGED
@@ -1,5 +1,6 @@
1
1
  #include "Reset.h"
2
2
  #include "WebPage.h"
3
+ #include "NetworkAccessManager.h"
3
4
 
4
5
  Reset::Reset(WebPage *page, QObject *parent) : Command(page, parent) {
5
6
  }
@@ -10,6 +11,8 @@ void Reset::start(QStringList &arguments) {
10
11
  page()->triggerAction(QWebPage::Stop);
11
12
  page()->currentFrame()->setHtml("<html><body></body></html>");
12
13
  page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
14
+ page()->setNetworkAccessManager(new NetworkAccessManager());
15
+ page()->setUserAgent(NULL);
13
16
  emit finished(new Response(true));
14
17
  }
15
18
 
data/src/Server.cpp CHANGED
@@ -11,11 +11,14 @@ Server::Server(QObject *parent) : QObject(parent) {
11
11
 
12
12
  bool Server::start() {
13
13
  connect(m_tcp_server, SIGNAL(newConnection()), this, SLOT(handleConnection()));
14
- return m_tcp_server->listen(QHostAddress::Any, 9200);
14
+ return m_tcp_server->listen(QHostAddress::Any, 0);
15
+ }
16
+
17
+ quint16 Server::server_port() const {
18
+ return m_tcp_server->serverPort();
15
19
  }
16
20
 
17
21
  void Server::handleConnection() {
18
22
  QTcpSocket *socket = m_tcp_server->nextPendingConnection();
19
23
  new Connection(socket, m_page, this);
20
24
  }
21
-
data/src/Server.h CHANGED
@@ -9,6 +9,7 @@ class Server : public QObject {
9
9
  public:
10
10
  Server(QObject *parent = 0);
11
11
  bool start();
12
+ quint16 server_port() const;
12
13
 
13
14
  public slots:
14
15
  void handleConnection();
data/src/Source.cpp CHANGED
@@ -7,7 +7,14 @@ Source::Source(WebPage *page, QObject *parent) : Command(page, parent) {
7
7
  void Source::start(QStringList &arguments) {
8
8
  Q_UNUSED(arguments)
9
9
 
10
- QString result = page()->currentFrame()->toHtml();
11
- emit finished(new Response(true, result));
10
+ QNetworkAccessManager* accessManager = page()->networkAccessManager();
11
+ QNetworkRequest request(page()->currentFrame()->url());
12
+ reply = accessManager->get(request);
13
+
14
+ connect(reply, SIGNAL(finished()), this, SLOT(sourceLoaded()));
15
+ }
16
+
17
+ void Source::sourceLoaded() {
18
+ emit finished(new Response(true, reply->readAll()));
12
19
  }
13
20
 
data/src/Source.h CHANGED
@@ -1,6 +1,7 @@
1
1
  #include "Command.h"
2
2
 
3
3
  class WebPage;
4
+ class QNetworkReply;
4
5
 
5
6
  class Source : public Command {
6
7
  Q_OBJECT
@@ -8,5 +9,11 @@ class Source : public Command {
8
9
  public:
9
10
  Source(WebPage *page, QObject *parent = 0);
10
11
  virtual void start(QStringList &arguments);
12
+
13
+ public slots:
14
+ void sourceLoaded();
15
+
16
+ private:
17
+ QNetworkReply *reply;
11
18
  };
12
19
 
data/src/WebPage.cpp CHANGED
@@ -1,9 +1,24 @@
1
1
  #include "WebPage.h"
2
2
  #include "JavascriptInvocation.h"
3
+ #include "NetworkAccessManager.h"
3
4
  #include <QResource>
4
5
  #include <iostream>
5
6
 
6
7
  WebPage::WebPage(QObject *parent) : QWebPage(parent) {
8
+ loadJavascript();
9
+ setUserStylesheet();
10
+
11
+ m_loading = false;
12
+
13
+ this->setNetworkAccessManager(new NetworkAccessManager());
14
+
15
+ connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
16
+ connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
17
+ connect(this, SIGNAL(frameCreated(QWebFrame *)),
18
+ this, SLOT(frameCreated(QWebFrame *)));
19
+ }
20
+
21
+ void WebPage::loadJavascript() {
7
22
  QResource javascript(":/capybara.js");
8
23
  if (javascript.isCompressed()) {
9
24
  QByteArray uncompressedBytes(qUncompress(javascript.data(), javascript.size()));
@@ -14,11 +29,24 @@ WebPage::WebPage(QObject *parent) : QWebPage(parent) {
14
29
  javascriptString[javascript.size()] = 0;
15
30
  m_capybaraJavascript = javascriptString;
16
31
  }
17
- m_loading = false;
18
- connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
19
- connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
20
- connect(this, SIGNAL(frameCreated(QWebFrame *)),
21
- this, SLOT(frameCreated(QWebFrame *)));
32
+ }
33
+
34
+ void WebPage::setUserStylesheet() {
35
+ QString data = QString("* { font-family: 'Arial' ! important; }").toUtf8().toBase64();
36
+ QUrl url = QUrl(QString("data:text/css;charset=utf-8;base64,") + data);
37
+ settings()->setUserStyleSheetUrl(url);
38
+ }
39
+
40
+ QString WebPage::userAgentForUrl(const QUrl &url ) const {
41
+ if (!m_userAgent.isEmpty()) {
42
+ return m_userAgent;
43
+ } else {
44
+ return QWebPage::userAgentForUrl(url);
45
+ }
46
+ }
47
+
48
+ void WebPage::setUserAgent(QString userAgent) {
49
+ m_userAgent = userAgent;
22
50
  }
23
51
 
24
52
  void WebPage::frameCreated(QWebFrame * frame) {
@@ -90,3 +118,49 @@ QString WebPage::failureString() {
90
118
  return QString("Unable to load URL: ") + currentFrame()->requestedUrl().toString();
91
119
  }
92
120
 
121
+ bool WebPage::render(const QString &fileName) {
122
+ QFileInfo fileInfo(fileName);
123
+ QDir dir;
124
+ dir.mkpath(fileInfo.absolutePath());
125
+
126
+ QSize viewportSize = this->viewportSize();
127
+ QSize pageSize = this->mainFrame()->contentsSize();
128
+ if (pageSize.isEmpty()) {
129
+ return false;
130
+ }
131
+
132
+ QImage buffer(pageSize, QImage::Format_ARGB32);
133
+ buffer.fill(qRgba(255, 255, 255, 0));
134
+
135
+ QPainter p(&buffer);
136
+ p.setRenderHint( QPainter::Antialiasing, true);
137
+ p.setRenderHint( QPainter::TextAntialiasing, true);
138
+ p.setRenderHint( QPainter::SmoothPixmapTransform, true);
139
+
140
+ this->setViewportSize(pageSize);
141
+ this->mainFrame()->render(&p);
142
+ p.end();
143
+ this->setViewportSize(viewportSize);
144
+
145
+ return buffer.save(fileName);
146
+ }
147
+
148
+ QString WebPage::chooseFile(QWebFrame *parentFrame, const QString &suggestedFile) {
149
+ Q_UNUSED(parentFrame);
150
+ Q_UNUSED(suggestedFile);
151
+
152
+ return getLastAttachedFileName();
153
+ }
154
+
155
+ bool WebPage::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) {
156
+ if (extension == ChooseMultipleFilesExtension) {
157
+ QStringList names = QStringList() << getLastAttachedFileName();
158
+ static_cast<ChooseMultipleFilesExtensionReturn*>(output)->fileNames = names;
159
+ return true;
160
+ }
161
+ return false;
162
+ }
163
+
164
+ QString WebPage::getLastAttachedFileName() {
165
+ return currentFrame()->evaluateJavaScript(QString("Capybara.lastAttachedFile")).toString();
166
+ }
data/src/WebPage.h CHANGED
@@ -8,6 +8,10 @@ class WebPage : public QWebPage {
8
8
  QVariant invokeCapybaraFunction(const char *name, QStringList &arguments);
9
9
  QVariant invokeCapybaraFunction(QString &name, QStringList &arguments);
10
10
  QString failureString();
11
+ QString userAgentForUrl(const QUrl &url ) const;
12
+ void setUserAgent(QString userAgent);
13
+ bool render(const QString &fileName);
14
+ virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
11
15
 
12
16
  public slots:
13
17
  bool shouldInterruptJavaScript();
@@ -22,9 +26,14 @@ class WebPage : public QWebPage {
22
26
  virtual void javaScriptAlert(QWebFrame *frame, const QString &message);
23
27
  virtual bool javaScriptConfirm(QWebFrame *frame, const QString &message);
24
28
  virtual bool javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result);
29
+ virtual QString chooseFile(QWebFrame * parentFrame, const QString &suggestedFile);
25
30
 
26
31
  private:
27
32
  QString m_capybaraJavascript;
33
+ QString m_userAgent;
28
34
  bool m_loading;
35
+ QString getLastAttachedFileName();
36
+ void loadJavascript();
37
+ void setUserStylesheet();
29
38
  };
30
39
 
data/src/body.cpp ADDED
@@ -0,0 +1,11 @@
1
+ #include "Body.h"
2
+ #include "WebPage.h"
3
+
4
+ Body::Body(WebPage *page, QObject *parent) : Command(page, parent) {
5
+ }
6
+
7
+ void Body::start(QStringList &arguments) {
8
+ Q_UNUSED(arguments);
9
+ QString result = page()->currentFrame()->toHtml();
10
+ emit finished(new Response(true, result));
11
+ }
data/src/capybara.js CHANGED
@@ -1,6 +1,7 @@
1
1
  Capybara = {
2
2
  nextIndex: 0,
3
3
  nodes: {},
4
+ lastAttachedFile: "",
4
5
 
5
6
  invoke: function () {
6
7
  return this[CapybaraInvocation.functionName].apply(this, CapybaraInvocation.arguments);
@@ -27,16 +28,22 @@ Capybara = {
27
28
  },
28
29
 
29
30
  text: function (index) {
30
- return this.nodes[index].innerText;
31
+ var node = this.nodes[index];
32
+ var type = (node.type || node.tagName).toLowerCase();
33
+ if (type == "textarea") {
34
+ return node.innerHTML;
35
+ } else {
36
+ return node.innerText;
37
+ }
31
38
  },
32
39
 
33
40
  attribute: function (index, name) {
34
41
  switch(name) {
35
- case 'checked':
42
+ case 'checked':
36
43
  return this.nodes[index].checked;
37
44
  break;
38
45
 
39
- case 'disabled':
46
+ case 'disabled':
40
47
  return this.nodes[index].disabled;
41
48
  break;
42
49
 
@@ -84,6 +91,10 @@ Capybara = {
84
91
  return true;
85
92
  },
86
93
 
94
+ selected: function (index) {
95
+ return this.nodes[index].selected;
96
+ },
97
+
87
98
  value: function(index) {
88
99
  return this.nodes[index].value;
89
100
  },
@@ -94,7 +105,8 @@ Capybara = {
94
105
  if (type == "text" || type == "textarea" || type == "password") {
95
106
  this.trigger(index, "focus");
96
107
  node.value = "";
97
- for(var strindex = 0; strindex < value.length; strindex++) {
108
+ var length = this.attribute(index, "maxlength") || value.length;
109
+ for(var strindex = 0; strindex < length; strindex++) {
98
110
  node.value += value[strindex];
99
111
  this.trigger(index, "keydown");
100
112
  this.keypress(index, false, false, false, false, 0, value[strindex]);
@@ -106,6 +118,9 @@ Capybara = {
106
118
  node.checked = (value == "true");
107
119
  this.trigger(index, "click");
108
120
  this.trigger(index, "change");
121
+ } else if(type == "file") {
122
+ this.lastAttachedFile = value;
123
+ this.trigger(index, "click");
109
124
  } else {
110
125
  node.value = value;
111
126
  }