capybara-webkit 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -4
- data/Appraisals +4 -0
- data/Gemfile.lock +3 -7
- data/NEWS.md +7 -0
- data/README.md +16 -118
- data/Rakefile +3 -1
- data/capybara-webkit.gemspec +1 -1
- data/gemfiles/2.1.gemfile.lock +2 -3
- data/gemfiles/2.2.gemfile.lock +2 -2
- data/gemfiles/2.3.gemfile.lock +2 -2
- data/gemfiles/2.4.gemfile +7 -0
- data/gemfiles/2.4.gemfile.lock +77 -0
- data/lib/capybara/webkit/browser.rb +38 -2
- data/lib/capybara/webkit/connection.rb +26 -3
- data/lib/capybara/webkit/driver.rb +74 -0
- data/lib/capybara/webkit/errors.rb +6 -0
- data/lib/capybara/webkit/version.rb +1 -1
- data/spec/connection_spec.rb +27 -0
- data/spec/driver_spec.rb +352 -12
- data/spec/selenium_compatibility_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/src/AcceptAlert.cpp +11 -0
- data/src/AcceptAlert.h +10 -0
- data/src/CommandFactory.cpp +2 -0
- data/src/Connection.cpp +8 -3
- data/src/Connection.h +1 -0
- data/src/FindModal.cpp +29 -0
- data/src/FindModal.h +16 -0
- data/src/NetworkAccessManager.cpp +6 -6
- data/src/NetworkAccessManager.h +1 -1
- data/src/SetConfirmAction.cpp +10 -2
- data/src/SetPromptAction.cpp +13 -2
- data/src/WebPage.cpp +105 -10
- data/src/WebPage.h +12 -2
- data/src/WebPageManager.cpp +6 -0
- data/src/capybara.js +9 -8
- data/src/find_command.h +2 -0
- data/src/webkit_server.pro +4 -0
- metadata +10 -4
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Capybara::Webkit, 'compatibility with selenium' do
|
4
4
|
include AppRunner
|
5
5
|
|
6
|
-
it 'generates the same events as selenium when filling out forms' do
|
6
|
+
it 'generates the same events as selenium when filling out forms', selenium_compatibility: true do
|
7
7
|
run_application_for_html(<<-HTML)
|
8
8
|
<html><body>
|
9
9
|
<form onsubmit="return false">
|
data/spec/spec_helper.rb
CHANGED
@@ -28,6 +28,7 @@ RSpec.configure do |c|
|
|
28
28
|
|
29
29
|
c.filter_run_excluding :skip_on_windows => !(RbConfig::CONFIG['host_os'] =~ /mingw32/).nil?
|
30
30
|
c.filter_run_excluding :skip_on_jruby => !defined?(::JRUBY_VERSION).nil?
|
31
|
+
c.filter_run_excluding :selenium_compatibility => (Capybara::VERSION =~ /^2\.4\./).nil?
|
31
32
|
|
32
33
|
# We can't support outerWidth and outerHeight without a visible window.
|
33
34
|
# We focus the next window instead of failing when closing windows.
|
data/src/AcceptAlert.cpp
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#include "AcceptAlert.h"
|
2
|
+
#include "SocketCommand.h"
|
3
|
+
#include "WebPage.h"
|
4
|
+
#include "WebPageManager.h"
|
5
|
+
|
6
|
+
AcceptAlert::AcceptAlert(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
|
7
|
+
}
|
8
|
+
|
9
|
+
void AcceptAlert::start() {
|
10
|
+
finish(true, page()->acceptAlert(arguments()[0]));
|
11
|
+
}
|
data/src/AcceptAlert.h
ADDED
data/src/CommandFactory.cpp
CHANGED
data/src/Connection.cpp
CHANGED
@@ -17,6 +17,7 @@ Connection::Connection(QTcpSocket *socket, WebPageManager *manager, QObject *par
|
|
17
17
|
m_commandFactory = new CommandFactory(m_manager, this);
|
18
18
|
m_commandParser = new CommandParser(socket, m_commandFactory, this);
|
19
19
|
m_pageSuccess = true;
|
20
|
+
m_pendingCommand = NULL;
|
20
21
|
connect(m_socket, SIGNAL(readyRead()), m_commandParser, SLOT(checkNext()));
|
21
22
|
connect(m_commandParser, SIGNAL(commandReady(Command *)), this, SLOT(commandReady(Command *)));
|
22
23
|
connect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
|
@@ -28,10 +29,13 @@ void Connection::commandReady(Command *command) {
|
|
28
29
|
}
|
29
30
|
|
30
31
|
void Connection::startCommand(Command *command) {
|
32
|
+
if (m_pendingCommand) {
|
33
|
+
m_pendingCommand->deleteLater();
|
34
|
+
}
|
31
35
|
if (m_pageSuccess) {
|
32
|
-
|
33
|
-
connect(
|
34
|
-
|
36
|
+
m_pendingCommand = new TimeoutCommand(new PageLoadingCommand(command, m_manager, this), m_manager, this);
|
37
|
+
connect(m_pendingCommand, SIGNAL(finished(Response *)), this, SLOT(finishCommand(Response *)));
|
38
|
+
m_pendingCommand->start();
|
35
39
|
} else {
|
36
40
|
writePageLoadFailure();
|
37
41
|
}
|
@@ -52,6 +56,7 @@ void Connection::finishCommand(Response *response) {
|
|
52
56
|
m_pageSuccess = true;
|
53
57
|
writeResponse(response);
|
54
58
|
sender()->deleteLater();
|
59
|
+
m_pendingCommand = NULL;
|
55
60
|
}
|
56
61
|
|
57
62
|
void Connection::writeResponse(Response *response) {
|
data/src/Connection.h
CHANGED
data/src/FindModal.cpp
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#include "FindModal.h"
|
2
|
+
#include "SocketCommand.h"
|
3
|
+
#include "WebPage.h"
|
4
|
+
#include "WebPageManager.h"
|
5
|
+
#include "ErrorMessage.h"
|
6
|
+
|
7
|
+
FindModal::FindModal(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
|
8
|
+
m_modalId = 0;
|
9
|
+
}
|
10
|
+
|
11
|
+
void FindModal::start() {
|
12
|
+
m_modalId = arguments()[0].toInt();
|
13
|
+
if (page()->modalCount() < m_modalId) {
|
14
|
+
connect(page(), SIGNAL(modalReady(int)), SLOT(handleModalReady(int)));
|
15
|
+
} else {
|
16
|
+
handleModalReady(m_modalId);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
void FindModal::handleModalReady(int modalId) {
|
21
|
+
if (modalId == m_modalId) {
|
22
|
+
sender()->disconnect(SIGNAL(modalReady(int)), this, SLOT(handleModalReady(int)));
|
23
|
+
if (page()->modalMessage(m_modalId).isNull()) {
|
24
|
+
finish(false, new ErrorMessage("ModalNotFound", ""));
|
25
|
+
} else {
|
26
|
+
finish(true, page()->modalMessage(m_modalId));
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
data/src/FindModal.h
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#include "SocketCommand.h"
|
2
|
+
|
3
|
+
class FindModal : public SocketCommand {
|
4
|
+
Q_OBJECT
|
5
|
+
|
6
|
+
public:
|
7
|
+
FindModal(WebPageManager *, QStringList &arguments, QObject *parent = 0);
|
8
|
+
virtual void start();
|
9
|
+
|
10
|
+
public slots:
|
11
|
+
void handleModalReady(int);
|
12
|
+
|
13
|
+
private:
|
14
|
+
int m_modalId;
|
15
|
+
};
|
16
|
+
|
@@ -74,19 +74,19 @@ void NetworkAccessManager::setUrlBlacklist(QStringList urlBlacklist) {
|
|
74
74
|
|
75
75
|
QStringListIterator iter(urlBlacklist);
|
76
76
|
while (iter.hasNext()) {
|
77
|
-
m_urlBlacklist <<
|
77
|
+
m_urlBlacklist << iter.next();
|
78
78
|
}
|
79
79
|
};
|
80
80
|
|
81
81
|
bool NetworkAccessManager::isBlacklisted(QUrl url) {
|
82
|
-
|
82
|
+
QString urlString = url.toString();
|
83
|
+
QStringListIterator iter(m_urlBlacklist);
|
83
84
|
|
84
85
|
while (iter.hasNext()) {
|
85
|
-
|
86
|
+
QRegExp blacklisted = QRegExp(iter.next());
|
87
|
+
blacklisted.setPatternSyntax(QRegExp::Wildcard);
|
86
88
|
|
87
|
-
if
|
88
|
-
return true;
|
89
|
-
} else if (blacklisted.path().isEmpty() && blacklisted.isParentOf(url)) {
|
89
|
+
if(urlString.contains(blacklisted)) {
|
90
90
|
return true;
|
91
91
|
}
|
92
92
|
}
|
data/src/NetworkAccessManager.h
CHANGED
@@ -21,7 +21,7 @@ class NetworkAccessManager : public QNetworkAccessManager {
|
|
21
21
|
QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice * outgoingData);
|
22
22
|
QString m_userName;
|
23
23
|
QString m_password;
|
24
|
-
|
24
|
+
QStringList m_urlBlacklist;
|
25
25
|
|
26
26
|
private:
|
27
27
|
void disableKeyChainLookup();
|
data/src/SetConfirmAction.cpp
CHANGED
@@ -6,6 +6,14 @@ SetConfirmAction::SetConfirmAction(WebPageManager *manager, QStringList &argumen
|
|
6
6
|
|
7
7
|
void SetConfirmAction::start()
|
8
8
|
{
|
9
|
-
|
10
|
-
|
9
|
+
QString index;
|
10
|
+
switch (arguments().length()) {
|
11
|
+
case 2:
|
12
|
+
index = page()->setConfirmAction(arguments()[0], arguments()[1]);
|
13
|
+
break;
|
14
|
+
default:
|
15
|
+
page()->setConfirmAction(arguments()[0]);
|
16
|
+
}
|
17
|
+
|
18
|
+
finish(true, index);
|
11
19
|
}
|
data/src/SetPromptAction.cpp
CHANGED
@@ -6,6 +6,17 @@ SetPromptAction::SetPromptAction(WebPageManager *manager, QStringList &arguments
|
|
6
6
|
|
7
7
|
void SetPromptAction::start()
|
8
8
|
{
|
9
|
-
|
10
|
-
|
9
|
+
QString index;
|
10
|
+
switch (arguments().length()) {
|
11
|
+
case 3:
|
12
|
+
index = page()->setPromptAction(arguments()[0], arguments()[1], arguments()[2]);
|
13
|
+
break;
|
14
|
+
case 2:
|
15
|
+
index = page()->setPromptAction(arguments()[0], arguments()[1]);
|
16
|
+
break;
|
17
|
+
default:
|
18
|
+
page()->setPromptAction(arguments()[0]);
|
19
|
+
}
|
20
|
+
|
21
|
+
finish(true, index);
|
11
22
|
}
|
data/src/WebPage.cpp
CHANGED
@@ -19,14 +19,13 @@ WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
|
|
19
19
|
m_failed = false;
|
20
20
|
m_manager = manager;
|
21
21
|
m_uuid = QUuid::createUuid().toString();
|
22
|
+
m_confirmAction = true;
|
23
|
+
m_promptAction = false;
|
22
24
|
|
23
25
|
setForwardUnsupportedContent(true);
|
24
26
|
loadJavascript();
|
25
27
|
setUserStylesheet();
|
26
28
|
|
27
|
-
m_confirm = true;
|
28
|
-
m_prompt = false;
|
29
|
-
m_prompt_text = QString();
|
30
29
|
this->setCustomNetworkAccessManager();
|
31
30
|
|
32
31
|
connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
|
@@ -41,6 +40,11 @@ WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
|
|
41
40
|
settings()->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
|
42
41
|
settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
|
43
42
|
|
43
|
+
if(QFileInfo("tmp").isDir()) {
|
44
|
+
settings()->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, true);
|
45
|
+
settings()->setOfflineWebApplicationCachePath("tmp");
|
46
|
+
}
|
47
|
+
|
44
48
|
createWindow();
|
45
49
|
}
|
46
50
|
|
@@ -180,26 +184,71 @@ void WebPage::javaScriptConsoleMessage(const QString &message, int lineNumber, c
|
|
180
184
|
void WebPage::javaScriptAlert(QWebFrame *frame, const QString &message) {
|
181
185
|
Q_UNUSED(frame);
|
182
186
|
m_alertMessages.append(message);
|
187
|
+
|
188
|
+
if (m_modalResponses.isEmpty()) {
|
189
|
+
m_modalMessages << QString();
|
190
|
+
} else {
|
191
|
+
QVariantMap alertResponse = m_modalResponses.takeLast();
|
192
|
+
bool expectedType = alertResponse["type"].toString() == "alert";
|
193
|
+
QRegExp expectedMessage = alertResponse["message"].toRegExp();
|
194
|
+
|
195
|
+
addModalMessage(expectedType, message, expectedMessage);
|
196
|
+
}
|
197
|
+
|
183
198
|
m_manager->logger() << "ALERT:" << qPrintable(message);
|
184
199
|
}
|
185
200
|
|
186
201
|
bool WebPage::javaScriptConfirm(QWebFrame *frame, const QString &message) {
|
187
202
|
Q_UNUSED(frame);
|
188
203
|
m_confirmMessages.append(message);
|
189
|
-
|
204
|
+
|
205
|
+
if (m_modalResponses.isEmpty()) {
|
206
|
+
m_modalMessages << QString();
|
207
|
+
return m_confirmAction;
|
208
|
+
} else {
|
209
|
+
QVariantMap confirmResponse = m_modalResponses.takeLast();
|
210
|
+
bool expectedType = confirmResponse["type"].toString() == "confirm";
|
211
|
+
QRegExp expectedMessage = confirmResponse["message"].toRegExp();
|
212
|
+
|
213
|
+
addModalMessage(expectedType, message, expectedMessage);
|
214
|
+
return expectedType &&
|
215
|
+
confirmResponse["action"].toBool() &&
|
216
|
+
message.contains(expectedMessage);
|
217
|
+
}
|
190
218
|
}
|
191
219
|
|
192
220
|
bool WebPage::javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result) {
|
193
221
|
Q_UNUSED(frame)
|
194
222
|
m_promptMessages.append(message);
|
195
|
-
|
196
|
-
|
223
|
+
|
224
|
+
bool action = false;
|
225
|
+
QString response;
|
226
|
+
|
227
|
+
if (m_modalResponses.isEmpty()) {
|
228
|
+
action = m_promptAction;
|
229
|
+
response = m_prompt_text;
|
230
|
+
m_modalMessages << QString();
|
231
|
+
} else {
|
232
|
+
QVariantMap promptResponse = m_modalResponses.takeLast();
|
233
|
+
bool expectedType = promptResponse["type"].toString() == "prompt";
|
234
|
+
QRegExp expectedMessage = promptResponse["message"].toRegExp();
|
235
|
+
|
236
|
+
action = expectedType &&
|
237
|
+
promptResponse["action"].toBool() &&
|
238
|
+
message.contains(expectedMessage);
|
239
|
+
response = promptResponse["response"].toString();
|
240
|
+
addModalMessage(expectedType, message, expectedMessage);
|
241
|
+
}
|
242
|
+
|
243
|
+
if (action) {
|
244
|
+
if (response.isNull()) {
|
197
245
|
*result = defaultValue;
|
198
246
|
} else {
|
199
|
-
*result =
|
247
|
+
*result = response;
|
200
248
|
}
|
201
249
|
}
|
202
|
-
|
250
|
+
|
251
|
+
return action;
|
203
252
|
}
|
204
253
|
|
205
254
|
void WebPage::loadStarted() {
|
@@ -376,15 +425,61 @@ void WebPage::remove() {
|
|
376
425
|
m_manager->removePage(this);
|
377
426
|
}
|
378
427
|
|
428
|
+
QString WebPage::setConfirmAction(QString action, QString message) {
|
429
|
+
QVariantMap confirmResponse;
|
430
|
+
confirmResponse["type"] = "confirm";
|
431
|
+
confirmResponse["action"] = (action=="Yes");
|
432
|
+
confirmResponse["message"] = QRegExp(message);
|
433
|
+
m_modalResponses << confirmResponse;
|
434
|
+
return QString::number(m_modalResponses.length());
|
435
|
+
}
|
436
|
+
|
379
437
|
void WebPage::setConfirmAction(QString action) {
|
380
|
-
|
438
|
+
m_confirmAction = (action == "Yes");
|
439
|
+
}
|
440
|
+
|
441
|
+
QString WebPage::setPromptAction(QString action, QString message, QString response) {
|
442
|
+
QVariantMap promptResponse;
|
443
|
+
promptResponse["type"] = "prompt";
|
444
|
+
promptResponse["action"] = (action == "Yes");
|
445
|
+
promptResponse["message"] = QRegExp(message);
|
446
|
+
promptResponse["response"] = response;
|
447
|
+
m_modalResponses << promptResponse;
|
448
|
+
return QString::number(m_modalResponses.length());
|
449
|
+
}
|
450
|
+
|
451
|
+
QString WebPage::setPromptAction(QString action, QString message) {
|
452
|
+
return setPromptAction(action, message, QString());
|
381
453
|
}
|
382
454
|
|
383
455
|
void WebPage::setPromptAction(QString action) {
|
384
|
-
|
456
|
+
m_promptAction = (action == "Yes");
|
385
457
|
}
|
386
458
|
|
387
459
|
void WebPage::setPromptText(QString text) {
|
388
460
|
m_prompt_text = text;
|
389
461
|
}
|
390
462
|
|
463
|
+
QString WebPage::acceptAlert(QString message) {
|
464
|
+
QVariantMap alertResponse;
|
465
|
+
alertResponse["type"] = "alert";
|
466
|
+
alertResponse["message"] = QRegExp(message);
|
467
|
+
m_modalResponses << alertResponse;
|
468
|
+
return QString::number(m_modalResponses.length());
|
469
|
+
}
|
470
|
+
|
471
|
+
int WebPage::modalCount() {
|
472
|
+
return m_modalMessages.length();
|
473
|
+
}
|
474
|
+
|
475
|
+
QString WebPage::modalMessage(int id) {
|
476
|
+
return m_modalMessages[id - 1];
|
477
|
+
}
|
478
|
+
|
479
|
+
void WebPage::addModalMessage(bool expectedType, const QString &message, const QRegExp &expectedMessage) {
|
480
|
+
if (expectedType && message.contains(expectedMessage))
|
481
|
+
m_modalMessages << message;
|
482
|
+
else
|
483
|
+
m_modalMessages << QString();
|
484
|
+
emit modalReady(m_modalMessages.length());
|
485
|
+
}
|
data/src/WebPage.h
CHANGED
@@ -23,8 +23,12 @@ class WebPage : public QWebPage {
|
|
23
23
|
QString userAgentForUrl(const QUrl &url ) const;
|
24
24
|
void setUserAgent(QString userAgent);
|
25
25
|
void setConfirmAction(QString action);
|
26
|
+
QString setConfirmAction(QString action, QString message);
|
27
|
+
QString setPromptAction(QString action, QString message, QString response);
|
28
|
+
QString setPromptAction(QString action, QString message);
|
26
29
|
void setPromptAction(QString action);
|
27
30
|
void setPromptText(QString action);
|
31
|
+
QString acceptAlert(QString);
|
28
32
|
int getLastStatus();
|
29
33
|
void setCustomNetworkAccessManager();
|
30
34
|
bool render(const QString &fileName, const QSize &minimumSize);
|
@@ -48,6 +52,8 @@ class WebPage : public QWebPage {
|
|
48
52
|
void mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button);
|
49
53
|
bool clickTest(QWebElement element, int absoluteX, int absoluteY);
|
50
54
|
void resize(int, int);
|
55
|
+
int modalCount();
|
56
|
+
QString modalMessage(int);
|
51
57
|
|
52
58
|
public slots:
|
53
59
|
bool shouldInterruptJavaScript();
|
@@ -65,6 +71,7 @@ class WebPage : public QWebPage {
|
|
65
71
|
void pageFinished(bool);
|
66
72
|
void requestCreated(QByteArray &url, QNetworkReply *reply);
|
67
73
|
void replyFinished(QNetworkReply *reply);
|
74
|
+
void modalReady(int);
|
68
75
|
|
69
76
|
protected:
|
70
77
|
virtual void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID);
|
@@ -82,8 +89,8 @@ class WebPage : public QWebPage {
|
|
82
89
|
QStringList getAttachedFileNames();
|
83
90
|
void loadJavascript();
|
84
91
|
void setUserStylesheet();
|
85
|
-
bool
|
86
|
-
bool
|
92
|
+
bool m_confirmAction;
|
93
|
+
bool m_promptAction;
|
87
94
|
QVariantList m_consoleMessages;
|
88
95
|
QVariantList m_alertMessages;
|
89
96
|
QVariantList m_confirmMessages;
|
@@ -94,6 +101,9 @@ class WebPage : public QWebPage {
|
|
94
101
|
QString m_errorPageMessage;
|
95
102
|
void setFrameProperties(QWebFrame *, QUrl &, NetworkReplyProxy *);
|
96
103
|
QPoint m_mousePosition;
|
104
|
+
QList<QVariantMap> m_modalResponses;
|
105
|
+
QStringList m_modalMessages;
|
106
|
+
void addModalMessage(bool, const QString &, const QRegExp &);
|
97
107
|
};
|
98
108
|
|
99
109
|
#endif //_WEBPAGE_H
|
data/src/WebPageManager.cpp
CHANGED
@@ -124,6 +124,12 @@ void WebPageManager::reset() {
|
|
124
124
|
WebPage *page = m_pages.takeFirst();
|
125
125
|
page->deleteLater();
|
126
126
|
}
|
127
|
+
|
128
|
+
qint64 size = QWebSettings::offlineWebApplicationCacheQuota();
|
129
|
+
// No public function was found to wrap the empty() call to
|
130
|
+
// WebCore::cacheStorage().empty()
|
131
|
+
QWebSettings::setOfflineWebApplicationCacheQuota(size);
|
132
|
+
|
127
133
|
createPage()->setFocus();
|
128
134
|
}
|
129
135
|
|