intentmedia-capybara-webkit 0.7.2.1
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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Appraisals +7 -0
- data/CONTRIBUTING.md +38 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +65 -0
- data/LICENSE +19 -0
- data/README.md +67 -0
- data/Rakefile +78 -0
- data/bin/Info.plist +22 -0
- data/capybara-webkit.gemspec +24 -0
- data/extconf.rb +2 -0
- data/gemfiles/1.0.gemfile +7 -0
- data/gemfiles/1.0.gemfile.lock +65 -0
- data/gemfiles/1.1.gemfile +7 -0
- data/gemfiles/1.1.gemfile.lock +65 -0
- data/lib/capybara-webkit.rb +1 -0
- data/lib/capybara/driver/webkit.rb +113 -0
- data/lib/capybara/driver/webkit/browser.rb +216 -0
- data/lib/capybara/driver/webkit/node.rb +118 -0
- data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
- data/lib/capybara/webkit.rb +11 -0
- data/lib/capybara_webkit_builder.rb +40 -0
- data/spec/browser_spec.rb +178 -0
- data/spec/driver_rendering_spec.rb +80 -0
- data/spec/driver_spec.rb +1048 -0
- data/spec/integration/driver_spec.rb +20 -0
- data/spec/integration/session_spec.rb +137 -0
- data/spec/self_signed_ssl_cert.rb +42 -0
- data/spec/spec_helper.rb +25 -0
- data/src/Body.h +12 -0
- data/src/ClearCookies.cpp +18 -0
- data/src/ClearCookies.h +11 -0
- data/src/Command.cpp +15 -0
- data/src/Command.h +29 -0
- data/src/CommandFactory.cpp +29 -0
- data/src/CommandFactory.h +16 -0
- data/src/CommandParser.cpp +68 -0
- data/src/CommandParser.h +29 -0
- data/src/Connection.cpp +82 -0
- data/src/Connection.h +36 -0
- data/src/Evaluate.cpp +84 -0
- data/src/Evaluate.h +22 -0
- data/src/Execute.cpp +16 -0
- data/src/Execute.h +12 -0
- data/src/Find.cpp +19 -0
- data/src/Find.h +13 -0
- data/src/FrameFocus.cpp +66 -0
- data/src/FrameFocus.h +28 -0
- data/src/GetCookies.cpp +22 -0
- data/src/GetCookies.h +14 -0
- data/src/Header.cpp +18 -0
- data/src/Header.h +11 -0
- data/src/Headers.cpp +11 -0
- data/src/Headers.h +12 -0
- data/src/JavascriptInvocation.cpp +14 -0
- data/src/JavascriptInvocation.h +19 -0
- data/src/NetworkAccessManager.cpp +25 -0
- data/src/NetworkAccessManager.h +18 -0
- data/src/NetworkCookieJar.cpp +101 -0
- data/src/NetworkCookieJar.h +15 -0
- data/src/Node.cpp +14 -0
- data/src/Node.h +13 -0
- data/src/Render.cpp +19 -0
- data/src/Render.h +12 -0
- data/src/Reset.cpp +20 -0
- data/src/Reset.h +12 -0
- data/src/Response.cpp +19 -0
- data/src/Response.h +13 -0
- data/src/Server.cpp +25 -0
- data/src/Server.h +21 -0
- data/src/SetCookie.cpp +18 -0
- data/src/SetCookie.h +11 -0
- data/src/SetProxy.cpp +24 -0
- data/src/SetProxy.h +11 -0
- data/src/Source.cpp +20 -0
- data/src/Source.h +19 -0
- data/src/Status.cpp +13 -0
- data/src/Status.h +12 -0
- data/src/UnsupportedContentHandler.cpp +32 -0
- data/src/UnsupportedContentHandler.h +18 -0
- data/src/Url.cpp +15 -0
- data/src/Url.h +12 -0
- data/src/Visit.cpp +21 -0
- data/src/Visit.h +15 -0
- data/src/WebPage.cpp +226 -0
- data/src/WebPage.h +54 -0
- data/src/body.cpp +11 -0
- data/src/capybara.js +205 -0
- data/src/find_command.h +24 -0
- data/src/main.cpp +34 -0
- data/src/webkit_server.pro +71 -0
- data/src/webkit_server.qrc +5 -0
- data/templates/Command.cpp +10 -0
- data/templates/Command.h +12 -0
- data/webkit_server.pro +4 -0
- metadata +246 -0
data/src/WebPage.h
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#include <QtWebKit>
|
2
|
+
|
3
|
+
class WebPage : public QWebPage {
|
4
|
+
Q_OBJECT
|
5
|
+
|
6
|
+
public:
|
7
|
+
WebPage(QObject *parent = 0);
|
8
|
+
QVariant invokeCapybaraFunction(const char *name, QStringList &arguments);
|
9
|
+
QVariant invokeCapybaraFunction(QString &name, QStringList &arguments);
|
10
|
+
QString failureString();
|
11
|
+
QString userAgentForUrl(const QUrl &url ) const;
|
12
|
+
void setUserAgent(QString userAgent);
|
13
|
+
int getLastStatus();
|
14
|
+
void resetResponseHeaders();
|
15
|
+
void setCustomNetworkAccessManager();
|
16
|
+
bool render(const QString &fileName);
|
17
|
+
virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
|
18
|
+
void setIgnoreSslErrors(bool ignore);
|
19
|
+
bool ignoreSslErrors();
|
20
|
+
|
21
|
+
public slots:
|
22
|
+
bool shouldInterruptJavaScript();
|
23
|
+
void injectJavascriptHelpers();
|
24
|
+
void loadStarted();
|
25
|
+
void loadFinished(bool);
|
26
|
+
bool isLoading() const;
|
27
|
+
QString pageHeaders();
|
28
|
+
void frameCreated(QWebFrame *);
|
29
|
+
void replyFinished(QNetworkReply *reply);
|
30
|
+
void ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &);
|
31
|
+
void handleUnsupportedContent(QNetworkReply *reply);
|
32
|
+
|
33
|
+
signals:
|
34
|
+
void pageFinished(bool);
|
35
|
+
|
36
|
+
protected:
|
37
|
+
virtual void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID);
|
38
|
+
virtual void javaScriptAlert(QWebFrame *frame, const QString &message);
|
39
|
+
virtual bool javaScriptConfirm(QWebFrame *frame, const QString &message);
|
40
|
+
virtual bool javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result);
|
41
|
+
virtual QString chooseFile(QWebFrame * parentFrame, const QString &suggestedFile);
|
42
|
+
|
43
|
+
private:
|
44
|
+
QString m_capybaraJavascript;
|
45
|
+
QString m_userAgent;
|
46
|
+
bool m_loading;
|
47
|
+
QString getLastAttachedFileName();
|
48
|
+
void loadJavascript();
|
49
|
+
void setUserStylesheet();
|
50
|
+
int m_lastStatus;
|
51
|
+
QString m_pageHeaders;
|
52
|
+
bool m_ignoreSslErrors;
|
53
|
+
};
|
54
|
+
|
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
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
Capybara = {
|
2
|
+
nextIndex: 0,
|
3
|
+
nodes: {},
|
4
|
+
lastAttachedFile: "",
|
5
|
+
|
6
|
+
invoke: function () {
|
7
|
+
return this[CapybaraInvocation.functionName].apply(this, CapybaraInvocation.arguments);
|
8
|
+
},
|
9
|
+
|
10
|
+
find: function (xpath) {
|
11
|
+
return this.findRelativeTo(document, xpath);
|
12
|
+
},
|
13
|
+
|
14
|
+
findWithin: function (index, xpath) {
|
15
|
+
return this.findRelativeTo(this.nodes[index], xpath);
|
16
|
+
},
|
17
|
+
|
18
|
+
findRelativeTo: function (reference, xpath) {
|
19
|
+
var iterator = document.evaluate(xpath, reference, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
|
20
|
+
var node;
|
21
|
+
var results = [];
|
22
|
+
while (node = iterator.iterateNext()) {
|
23
|
+
this.nextIndex++;
|
24
|
+
this.nodes[this.nextIndex] = node;
|
25
|
+
results.push(this.nextIndex);
|
26
|
+
}
|
27
|
+
return results.join(",");
|
28
|
+
},
|
29
|
+
|
30
|
+
isAttached: function(index) {
|
31
|
+
return document.evaluate("ancestor-or-self::html", this.nodes[index], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
|
32
|
+
},
|
33
|
+
|
34
|
+
text: function (index) {
|
35
|
+
var node = this.nodes[index];
|
36
|
+
var type = (node.type || node.tagName).toLowerCase();
|
37
|
+
if (type == "textarea") {
|
38
|
+
return node.innerHTML;
|
39
|
+
} else {
|
40
|
+
return node.innerText;
|
41
|
+
}
|
42
|
+
},
|
43
|
+
|
44
|
+
attribute: function (index, name) {
|
45
|
+
switch(name) {
|
46
|
+
case 'checked':
|
47
|
+
return this.nodes[index].checked;
|
48
|
+
break;
|
49
|
+
|
50
|
+
case 'disabled':
|
51
|
+
return this.nodes[index].disabled;
|
52
|
+
break;
|
53
|
+
|
54
|
+
default:
|
55
|
+
return this.nodes[index].getAttribute(name);
|
56
|
+
}
|
57
|
+
},
|
58
|
+
|
59
|
+
tagName: function(index) {
|
60
|
+
return this.nodes[index].tagName.toLowerCase();
|
61
|
+
},
|
62
|
+
|
63
|
+
click: function (index) {
|
64
|
+
var clickEvent = document.createEvent('MouseEvents');
|
65
|
+
clickEvent.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
66
|
+
this.nodes[index].dispatchEvent(clickEvent);
|
67
|
+
},
|
68
|
+
|
69
|
+
trigger: function (index, eventName) {
|
70
|
+
var eventObject = document.createEvent("HTMLEvents");
|
71
|
+
eventObject.initEvent(eventName, true, true);
|
72
|
+
this.nodes[index].dispatchEvent(eventObject);
|
73
|
+
},
|
74
|
+
|
75
|
+
keypress: function(index, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
|
76
|
+
var eventObject = document.createEvent("Events");
|
77
|
+
eventObject.initEvent('keypress', true, true);
|
78
|
+
eventObject.window = window;
|
79
|
+
eventObject.altKey = altKey;
|
80
|
+
eventObject.ctrlKey = ctrlKey;
|
81
|
+
eventObject.shiftKey = shiftKey;
|
82
|
+
eventObject.metaKey = metaKey;
|
83
|
+
eventObject.keyCode = keyCode;
|
84
|
+
eventObject.charCode = charCode;
|
85
|
+
this.nodes[index].dispatchEvent(eventObject);
|
86
|
+
},
|
87
|
+
|
88
|
+
visible: function (index) {
|
89
|
+
var element = this.nodes[index];
|
90
|
+
while (element) {
|
91
|
+
if (element.ownerDocument.defaultView.getComputedStyle(element, null).getPropertyValue("display") == 'none')
|
92
|
+
return false;
|
93
|
+
element = element.parentElement;
|
94
|
+
}
|
95
|
+
return true;
|
96
|
+
},
|
97
|
+
|
98
|
+
selected: function (index) {
|
99
|
+
return this.nodes[index].selected;
|
100
|
+
},
|
101
|
+
|
102
|
+
value: function(index) {
|
103
|
+
return this.nodes[index].value;
|
104
|
+
},
|
105
|
+
|
106
|
+
set: function(index, value) {
|
107
|
+
var node = this.nodes[index];
|
108
|
+
var type = (node.type || node.tagName).toLowerCase();
|
109
|
+
if (type == "text" || type == "textarea" || type == "password") {
|
110
|
+
this.trigger(index, "focus");
|
111
|
+
node.value = "";
|
112
|
+
var maxLength = this.attribute(index, "maxlength"),
|
113
|
+
length;
|
114
|
+
if (maxLength && value.length > maxLength) {
|
115
|
+
length = maxLength;
|
116
|
+
} else {
|
117
|
+
length = value.length;
|
118
|
+
}
|
119
|
+
|
120
|
+
for(var strindex = 0; strindex < length; strindex++) {
|
121
|
+
node.value += value[strindex];
|
122
|
+
this.trigger(index, "keydown");
|
123
|
+
this.keypress(index, false, false, false, false, 0, value[strindex]);
|
124
|
+
this.trigger(index, "keyup");
|
125
|
+
}
|
126
|
+
this.trigger(index, "change");
|
127
|
+
this.trigger(index, "blur");
|
128
|
+
} else if(type == "checkbox" || type == "radio") {
|
129
|
+
node.checked = (value == "true");
|
130
|
+
this.trigger(index, "click");
|
131
|
+
this.trigger(index, "change");
|
132
|
+
} else if(type == "file") {
|
133
|
+
this.lastAttachedFile = value;
|
134
|
+
this.trigger(index, "click");
|
135
|
+
} else {
|
136
|
+
node.value = value;
|
137
|
+
}
|
138
|
+
},
|
139
|
+
|
140
|
+
selectOption: function(index) {
|
141
|
+
this.nodes[index].selected = true;
|
142
|
+
this.nodes[index].setAttribute("selected", "selected");
|
143
|
+
this.trigger(index, "change");
|
144
|
+
},
|
145
|
+
|
146
|
+
unselectOption: function(index) {
|
147
|
+
this.nodes[index].selected = false;
|
148
|
+
this.nodes[index].removeAttribute("selected");
|
149
|
+
this.trigger(index, "change");
|
150
|
+
},
|
151
|
+
|
152
|
+
centerPostion: function(element) {
|
153
|
+
this.reflow(element);
|
154
|
+
var rect = element.getBoundingClientRect();
|
155
|
+
var position = {
|
156
|
+
x: rect.width / 2,
|
157
|
+
y: rect.height / 2
|
158
|
+
};
|
159
|
+
do {
|
160
|
+
position.x += element.offsetLeft;
|
161
|
+
position.y += element.offsetTop;
|
162
|
+
} while ((element = element.offsetParent));
|
163
|
+
position.x = Math.floor(position.x), position.y = Math.floor(position.y);
|
164
|
+
|
165
|
+
return position;
|
166
|
+
},
|
167
|
+
|
168
|
+
reflow: function(element, force) {
|
169
|
+
if (force || element.offsetWidth === 0) {
|
170
|
+
var prop, oldStyle = {}, newStyle = {position: "absolute", visibility : "hidden", display: "block" };
|
171
|
+
for (prop in newStyle) {
|
172
|
+
oldStyle[prop] = element.style[prop];
|
173
|
+
element.style[prop] = newStyle[prop];
|
174
|
+
}
|
175
|
+
element.offsetWidth, element.offsetHeight; // force reflow
|
176
|
+
for (prop in oldStyle)
|
177
|
+
element.style[prop] = oldStyle[prop];
|
178
|
+
}
|
179
|
+
},
|
180
|
+
|
181
|
+
dragTo: function (index, targetIndex) {
|
182
|
+
var element = this.nodes[index], target = this.nodes[targetIndex];
|
183
|
+
var position = this.centerPostion(element);
|
184
|
+
var options = {
|
185
|
+
clientX: position.x,
|
186
|
+
clientY: position.y
|
187
|
+
};
|
188
|
+
var mouseTrigger = function(eventName, options) {
|
189
|
+
var eventObject = document.createEvent("MouseEvents");
|
190
|
+
eventObject.initMouseEvent(eventName, true, true, window, 0, 0, 0, options.clientX || 0, options.clientY || 0, false, false, false, false, 0, null);
|
191
|
+
element.dispatchEvent(eventObject);
|
192
|
+
}
|
193
|
+
mouseTrigger('mousedown', options);
|
194
|
+
options.clientX += 1, options.clientY += 1;
|
195
|
+
mouseTrigger('mousemove', options);
|
196
|
+
|
197
|
+
position = this.centerPostion(target), options = {
|
198
|
+
clientX: position.x,
|
199
|
+
clientY: position.y
|
200
|
+
};
|
201
|
+
mouseTrigger('mousemove', options);
|
202
|
+
mouseTrigger('mouseup', options);
|
203
|
+
}
|
204
|
+
};
|
205
|
+
|
data/src/find_command.h
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#define CHECK_COMMAND(expectedName) \
|
2
|
+
if (strcmp(#expectedName, name) == 0) { \
|
3
|
+
return new expectedName(m_page, this); \
|
4
|
+
}
|
5
|
+
|
6
|
+
CHECK_COMMAND(Visit)
|
7
|
+
CHECK_COMMAND(Find)
|
8
|
+
CHECK_COMMAND(Reset)
|
9
|
+
CHECK_COMMAND(Node)
|
10
|
+
CHECK_COMMAND(Url)
|
11
|
+
CHECK_COMMAND(Source)
|
12
|
+
CHECK_COMMAND(Evaluate)
|
13
|
+
CHECK_COMMAND(Execute)
|
14
|
+
CHECK_COMMAND(FrameFocus)
|
15
|
+
CHECK_COMMAND(Header)
|
16
|
+
CHECK_COMMAND(Render)
|
17
|
+
CHECK_COMMAND(Body)
|
18
|
+
CHECK_COMMAND(Status)
|
19
|
+
CHECK_COMMAND(Headers)
|
20
|
+
CHECK_COMMAND(SetCookie)
|
21
|
+
CHECK_COMMAND(ClearCookies)
|
22
|
+
CHECK_COMMAND(GetCookies)
|
23
|
+
CHECK_COMMAND(Headers)
|
24
|
+
CHECK_COMMAND(SetProxy)
|
data/src/main.cpp
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#include "Server.h"
|
2
|
+
#include <QtGui>
|
3
|
+
#include <iostream>
|
4
|
+
#ifdef Q_OS_UNIX
|
5
|
+
#include <unistd.h>
|
6
|
+
#endif
|
7
|
+
|
8
|
+
int main(int argc, char **argv) {
|
9
|
+
#ifdef Q_OS_UNIX
|
10
|
+
if (setpgid(0, 0) < 0) {
|
11
|
+
std::cerr << "Unable to set new process group." << std::endl;
|
12
|
+
return 1;
|
13
|
+
}
|
14
|
+
#endif
|
15
|
+
|
16
|
+
QApplication app(argc, argv);
|
17
|
+
app.setApplicationName("capybara-webkit");
|
18
|
+
app.setOrganizationName("thoughtbot, inc");
|
19
|
+
app.setOrganizationDomain("thoughtbot.com");
|
20
|
+
|
21
|
+
QStringList args = app.arguments();
|
22
|
+
bool ignoreSslErrors = args.contains("--ignore-ssl-errors");
|
23
|
+
|
24
|
+
Server server(0, ignoreSslErrors);
|
25
|
+
|
26
|
+
if (server.start()) {
|
27
|
+
std::cout << "Capybara-webkit server started, listening on port: " << server.server_port() << std::endl;
|
28
|
+
return app.exec();
|
29
|
+
} else {
|
30
|
+
std::cerr << "Couldn't start capybara-webkit server" << std::endl;
|
31
|
+
return 1;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
TEMPLATE = app
|
2
|
+
TARGET = webkit_server
|
3
|
+
DESTDIR = .
|
4
|
+
HEADERS = \
|
5
|
+
WebPage.h \
|
6
|
+
Server.h \
|
7
|
+
Connection.h \
|
8
|
+
Command.h \
|
9
|
+
Visit.h \
|
10
|
+
Find.h \
|
11
|
+
Reset.h \
|
12
|
+
Node.h \
|
13
|
+
JavascriptInvocation.h \
|
14
|
+
Url.h \
|
15
|
+
Source.h \
|
16
|
+
Evaluate.h \
|
17
|
+
Execute.h \
|
18
|
+
FrameFocus.h \
|
19
|
+
Response.h \
|
20
|
+
NetworkAccessManager.h \
|
21
|
+
NetworkCookieJar.h \
|
22
|
+
Header.h \
|
23
|
+
Render.h \
|
24
|
+
body.h \
|
25
|
+
Status.h \
|
26
|
+
Headers.h \
|
27
|
+
UnsupportedContentHandler.h \
|
28
|
+
SetCookie.h \
|
29
|
+
ClearCookies.h \
|
30
|
+
GetCookies.h \
|
31
|
+
CommandParser.h \
|
32
|
+
CommandFactory.h \
|
33
|
+
SetProxy.h \
|
34
|
+
|
35
|
+
SOURCES = \
|
36
|
+
main.cpp \
|
37
|
+
WebPage.cpp \
|
38
|
+
Server.cpp \
|
39
|
+
Connection.cpp \
|
40
|
+
Command.cpp \
|
41
|
+
Visit.cpp \
|
42
|
+
Find.cpp \
|
43
|
+
Reset.cpp \
|
44
|
+
Node.cpp \
|
45
|
+
JavascriptInvocation.cpp \
|
46
|
+
Url.cpp \
|
47
|
+
Source.cpp \
|
48
|
+
Evaluate.cpp \
|
49
|
+
Execute.cpp \
|
50
|
+
FrameFocus.cpp \
|
51
|
+
Response.cpp \
|
52
|
+
NetworkAccessManager.cpp \
|
53
|
+
NetworkCookieJar.cpp \
|
54
|
+
Header.cpp \
|
55
|
+
Render.cpp \
|
56
|
+
body.cpp \
|
57
|
+
Status.cpp \
|
58
|
+
Headers.cpp \
|
59
|
+
UnsupportedContentHandler.cpp \
|
60
|
+
SetCookie.cpp \
|
61
|
+
ClearCookies.cpp \
|
62
|
+
GetCookies.cpp \
|
63
|
+
CommandParser.cpp \
|
64
|
+
CommandFactory.cpp \
|
65
|
+
SetProxy.cpp \
|
66
|
+
|
67
|
+
RESOURCES = webkit_server.qrc
|
68
|
+
QT += network webkit
|
69
|
+
CONFIG += console
|
70
|
+
CONFIG -= app_bundle
|
71
|
+
|
data/templates/Command.h
ADDED
data/webkit_server.pro
ADDED