capybara-webkit 1.2.0 → 1.3.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.
- 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
|
|