intentmedia-capybara-webkit 0.7.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/Appraisals +7 -0
  4. data/CONTRIBUTING.md +38 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +65 -0
  7. data/LICENSE +19 -0
  8. data/README.md +67 -0
  9. data/Rakefile +78 -0
  10. data/bin/Info.plist +22 -0
  11. data/capybara-webkit.gemspec +24 -0
  12. data/extconf.rb +2 -0
  13. data/gemfiles/1.0.gemfile +7 -0
  14. data/gemfiles/1.0.gemfile.lock +65 -0
  15. data/gemfiles/1.1.gemfile +7 -0
  16. data/gemfiles/1.1.gemfile.lock +65 -0
  17. data/lib/capybara-webkit.rb +1 -0
  18. data/lib/capybara/driver/webkit.rb +113 -0
  19. data/lib/capybara/driver/webkit/browser.rb +216 -0
  20. data/lib/capybara/driver/webkit/node.rb +118 -0
  21. data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
  22. data/lib/capybara/webkit.rb +11 -0
  23. data/lib/capybara_webkit_builder.rb +40 -0
  24. data/spec/browser_spec.rb +178 -0
  25. data/spec/driver_rendering_spec.rb +80 -0
  26. data/spec/driver_spec.rb +1048 -0
  27. data/spec/integration/driver_spec.rb +20 -0
  28. data/spec/integration/session_spec.rb +137 -0
  29. data/spec/self_signed_ssl_cert.rb +42 -0
  30. data/spec/spec_helper.rb +25 -0
  31. data/src/Body.h +12 -0
  32. data/src/ClearCookies.cpp +18 -0
  33. data/src/ClearCookies.h +11 -0
  34. data/src/Command.cpp +15 -0
  35. data/src/Command.h +29 -0
  36. data/src/CommandFactory.cpp +29 -0
  37. data/src/CommandFactory.h +16 -0
  38. data/src/CommandParser.cpp +68 -0
  39. data/src/CommandParser.h +29 -0
  40. data/src/Connection.cpp +82 -0
  41. data/src/Connection.h +36 -0
  42. data/src/Evaluate.cpp +84 -0
  43. data/src/Evaluate.h +22 -0
  44. data/src/Execute.cpp +16 -0
  45. data/src/Execute.h +12 -0
  46. data/src/Find.cpp +19 -0
  47. data/src/Find.h +13 -0
  48. data/src/FrameFocus.cpp +66 -0
  49. data/src/FrameFocus.h +28 -0
  50. data/src/GetCookies.cpp +22 -0
  51. data/src/GetCookies.h +14 -0
  52. data/src/Header.cpp +18 -0
  53. data/src/Header.h +11 -0
  54. data/src/Headers.cpp +11 -0
  55. data/src/Headers.h +12 -0
  56. data/src/JavascriptInvocation.cpp +14 -0
  57. data/src/JavascriptInvocation.h +19 -0
  58. data/src/NetworkAccessManager.cpp +25 -0
  59. data/src/NetworkAccessManager.h +18 -0
  60. data/src/NetworkCookieJar.cpp +101 -0
  61. data/src/NetworkCookieJar.h +15 -0
  62. data/src/Node.cpp +14 -0
  63. data/src/Node.h +13 -0
  64. data/src/Render.cpp +19 -0
  65. data/src/Render.h +12 -0
  66. data/src/Reset.cpp +20 -0
  67. data/src/Reset.h +12 -0
  68. data/src/Response.cpp +19 -0
  69. data/src/Response.h +13 -0
  70. data/src/Server.cpp +25 -0
  71. data/src/Server.h +21 -0
  72. data/src/SetCookie.cpp +18 -0
  73. data/src/SetCookie.h +11 -0
  74. data/src/SetProxy.cpp +24 -0
  75. data/src/SetProxy.h +11 -0
  76. data/src/Source.cpp +20 -0
  77. data/src/Source.h +19 -0
  78. data/src/Status.cpp +13 -0
  79. data/src/Status.h +12 -0
  80. data/src/UnsupportedContentHandler.cpp +32 -0
  81. data/src/UnsupportedContentHandler.h +18 -0
  82. data/src/Url.cpp +15 -0
  83. data/src/Url.h +12 -0
  84. data/src/Visit.cpp +21 -0
  85. data/src/Visit.h +15 -0
  86. data/src/WebPage.cpp +226 -0
  87. data/src/WebPage.h +54 -0
  88. data/src/body.cpp +11 -0
  89. data/src/capybara.js +205 -0
  90. data/src/find_command.h +24 -0
  91. data/src/main.cpp +34 -0
  92. data/src/webkit_server.pro +71 -0
  93. data/src/webkit_server.qrc +5 -0
  94. data/templates/Command.cpp +10 -0
  95. data/templates/Command.h +12 -0
  96. data/webkit_server.pro +4 -0
  97. 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
+
@@ -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
+
@@ -0,0 +1,5 @@
1
+ <RCC>
2
+ <qresource prefix="/">
3
+ <file>capybara.js</file>
4
+ </qresource>
5
+ </RCC>
@@ -0,0 +1,10 @@
1
+ #include "NAME.h"
2
+ #include "WebPage.h"
3
+
4
+ NAME::NAME(WebPage *page, QObject *parent) : Command(page, parent) {
5
+ }
6
+
7
+ void NAME::start(QStringList &arguments) {
8
+ Q_UNUSED(arguments);
9
+ }
10
+
@@ -0,0 +1,12 @@
1
+ #include "Command.h"
2
+
3
+ class WebPage;
4
+
5
+ class NAME : public Command {
6
+ Q_OBJECT
7
+
8
+ public:
9
+ NAME(WebPage *page, QObject *parent = 0);
10
+ virtual void start(QStringList &arguments);
11
+ };
12
+
data/webkit_server.pro ADDED
@@ -0,0 +1,4 @@
1
+ TEMPLATE = subdirs
2
+ CONFIG += ordered
3
+ SUBDIRS += src/webkit_server.pro
4
+