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.
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
+