imseng-capybara-webkit 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/.gitignore +21 -0
  2. data/.rspec +2 -0
  3. data/Appraisals +7 -0
  4. data/CONTRIBUTING.md +47 -0
  5. data/ChangeLog +70 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +19 -0
  8. data/NEWS.md +36 -0
  9. data/README.md +114 -0
  10. data/Rakefile +65 -0
  11. data/bin/Info.plist +22 -0
  12. data/capybara-webkit.gemspec +28 -0
  13. data/extconf.rb +2 -0
  14. data/gemfiles/1.0.gemfile +7 -0
  15. data/gemfiles/1.0.gemfile.lock +70 -0
  16. data/gemfiles/1.1.gemfile +7 -0
  17. data/gemfiles/1.1.gemfile.lock +70 -0
  18. data/lib/capybara/driver/webkit/browser.rb +164 -0
  19. data/lib/capybara/driver/webkit/connection.rb +120 -0
  20. data/lib/capybara/driver/webkit/cookie_jar.rb +55 -0
  21. data/lib/capybara/driver/webkit/node.rb +118 -0
  22. data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
  23. data/lib/capybara/driver/webkit/version.rb +7 -0
  24. data/lib/capybara/driver/webkit.rb +136 -0
  25. data/lib/capybara/webkit/matchers.rb +37 -0
  26. data/lib/capybara/webkit.rb +13 -0
  27. data/lib/capybara-webkit.rb +1 -0
  28. data/lib/capybara_webkit_builder.rb +68 -0
  29. data/spec/browser_spec.rb +173 -0
  30. data/spec/capybara_webkit_builder_spec.rb +37 -0
  31. data/spec/connection_spec.rb +54 -0
  32. data/spec/cookie_jar_spec.rb +48 -0
  33. data/spec/driver_rendering_spec.rb +80 -0
  34. data/spec/driver_resize_window_spec.rb +59 -0
  35. data/spec/driver_spec.rb +1552 -0
  36. data/spec/integration/driver_spec.rb +20 -0
  37. data/spec/integration/session_spec.rb +137 -0
  38. data/spec/self_signed_ssl_cert.rb +42 -0
  39. data/spec/spec_helper.rb +46 -0
  40. data/src/Body.h +12 -0
  41. data/src/ClearCookies.cpp +15 -0
  42. data/src/ClearCookies.h +11 -0
  43. data/src/Command.cpp +19 -0
  44. data/src/Command.h +31 -0
  45. data/src/CommandFactory.cpp +37 -0
  46. data/src/CommandFactory.h +16 -0
  47. data/src/CommandParser.cpp +76 -0
  48. data/src/CommandParser.h +33 -0
  49. data/src/Connection.cpp +71 -0
  50. data/src/Connection.h +37 -0
  51. data/src/ConsoleMessages.cpp +10 -0
  52. data/src/ConsoleMessages.h +12 -0
  53. data/src/CurrentUrl.cpp +68 -0
  54. data/src/CurrentUrl.h +16 -0
  55. data/src/Evaluate.cpp +84 -0
  56. data/src/Evaluate.h +22 -0
  57. data/src/Execute.cpp +16 -0
  58. data/src/Execute.h +12 -0
  59. data/src/Find.cpp +19 -0
  60. data/src/Find.h +13 -0
  61. data/src/FrameFocus.cpp +66 -0
  62. data/src/FrameFocus.h +28 -0
  63. data/src/GetCookies.cpp +20 -0
  64. data/src/GetCookies.h +14 -0
  65. data/src/Header.cpp +18 -0
  66. data/src/Header.h +11 -0
  67. data/src/Headers.cpp +10 -0
  68. data/src/Headers.h +12 -0
  69. data/src/IgnoreSslErrors.cpp +12 -0
  70. data/src/IgnoreSslErrors.h +12 -0
  71. data/src/JavascriptInvocation.cpp +14 -0
  72. data/src/JavascriptInvocation.h +19 -0
  73. data/src/NetworkAccessManager.cpp +29 -0
  74. data/src/NetworkAccessManager.h +19 -0
  75. data/src/NetworkCookieJar.cpp +101 -0
  76. data/src/NetworkCookieJar.h +15 -0
  77. data/src/Node.cpp +14 -0
  78. data/src/Node.h +13 -0
  79. data/src/NullCommand.cpp +10 -0
  80. data/src/NullCommand.h +11 -0
  81. data/src/PageLoadingCommand.cpp +46 -0
  82. data/src/PageLoadingCommand.h +40 -0
  83. data/src/Render.cpp +18 -0
  84. data/src/Render.h +12 -0
  85. data/src/RequestedUrl.cpp +12 -0
  86. data/src/RequestedUrl.h +12 -0
  87. data/src/Reset.cpp +29 -0
  88. data/src/Reset.h +15 -0
  89. data/src/ResizeWindow.cpp +16 -0
  90. data/src/ResizeWindow.h +12 -0
  91. data/src/Response.cpp +24 -0
  92. data/src/Response.h +15 -0
  93. data/src/Server.cpp +24 -0
  94. data/src/Server.h +21 -0
  95. data/src/SetCookie.cpp +16 -0
  96. data/src/SetCookie.h +11 -0
  97. data/src/SetProxy.cpp +22 -0
  98. data/src/SetProxy.h +11 -0
  99. data/src/Source.cpp +18 -0
  100. data/src/Source.h +19 -0
  101. data/src/Status.cpp +12 -0
  102. data/src/Status.h +12 -0
  103. data/src/UnsupportedContentHandler.cpp +32 -0
  104. data/src/UnsupportedContentHandler.h +18 -0
  105. data/src/Url.cpp +12 -0
  106. data/src/Url.h +12 -0
  107. data/src/Visit.cpp +12 -0
  108. data/src/Visit.h +12 -0
  109. data/src/WebPage.cpp +239 -0
  110. data/src/WebPage.h +58 -0
  111. data/src/body.cpp +10 -0
  112. data/src/capybara.js +315 -0
  113. data/src/find_command.h +29 -0
  114. data/src/main.cpp +33 -0
  115. data/src/webkit_server.pro +85 -0
  116. data/src/webkit_server.qrc +5 -0
  117. data/templates/Command.cpp +10 -0
  118. data/templates/Command.h +12 -0
  119. data/webkit_server.pro +4 -0
  120. metadata +298 -0
data/src/WebPage.cpp ADDED
@@ -0,0 +1,239 @@
1
+ #include "WebPage.h"
2
+ #include "JavascriptInvocation.h"
3
+ #include "NetworkAccessManager.h"
4
+ #include "NetworkCookieJar.h"
5
+ #include "UnsupportedContentHandler.h"
6
+ #include <QResource>
7
+ #include <iostream>
8
+
9
+ WebPage::WebPage(QObject *parent) : QWebPage(parent) {
10
+ setForwardUnsupportedContent(true);
11
+ loadJavascript();
12
+ setUserStylesheet();
13
+
14
+ m_loading = false;
15
+ m_ignoreSslErrors = false;
16
+ this->setCustomNetworkAccessManager();
17
+
18
+ connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
19
+ connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
20
+ connect(this, SIGNAL(frameCreated(QWebFrame *)),
21
+ this, SLOT(frameCreated(QWebFrame *)));
22
+ connect(this, SIGNAL(unsupportedContent(QNetworkReply*)),
23
+ this, SLOT(handleUnsupportedContent(QNetworkReply*)));
24
+ resetWindowSize();
25
+ }
26
+
27
+ void WebPage::resetWindowSize() {
28
+ this->setViewportSize(QSize(1680, 1050));
29
+ this->settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
30
+ }
31
+
32
+ void WebPage::setCustomNetworkAccessManager() {
33
+ NetworkAccessManager *manager = new NetworkAccessManager();
34
+ manager->setCookieJar(new NetworkCookieJar());
35
+ this->setNetworkAccessManager(manager);
36
+ connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
37
+ connect(manager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)),
38
+ this, SLOT(handleSslErrorsForReply(QNetworkReply *, QList<QSslError>)));
39
+ }
40
+
41
+ void WebPage::loadJavascript() {
42
+ QResource javascript(":/capybara.js");
43
+ if (javascript.isCompressed()) {
44
+ QByteArray uncompressedBytes(qUncompress(javascript.data(), javascript.size()));
45
+ m_capybaraJavascript = QString(uncompressedBytes);
46
+ } else {
47
+ char * javascriptString = new char[javascript.size() + 1];
48
+ strcpy(javascriptString, (const char *)javascript.data());
49
+ javascriptString[javascript.size()] = 0;
50
+ m_capybaraJavascript = javascriptString;
51
+ }
52
+ }
53
+
54
+ void WebPage::setUserStylesheet() {
55
+ QString data = QString("* { font-family: 'Arial' ! important; }").toUtf8().toBase64();
56
+ QUrl url = QUrl(QString("data:text/css;charset=utf-8;base64,") + data);
57
+ settings()->setUserStyleSheetUrl(url);
58
+ }
59
+
60
+ QString WebPage::userAgentForUrl(const QUrl &url ) const {
61
+ if (!m_userAgent.isEmpty()) {
62
+ return m_userAgent;
63
+ } else {
64
+ return QWebPage::userAgentForUrl(url);
65
+ }
66
+ }
67
+
68
+ QString WebPage::consoleMessages() {
69
+ return m_consoleMessages.join("\n");
70
+ }
71
+
72
+ void WebPage::setUserAgent(QString userAgent) {
73
+ m_userAgent = userAgent;
74
+ }
75
+
76
+ void WebPage::frameCreated(QWebFrame * frame) {
77
+ connect(frame, SIGNAL(javaScriptWindowObjectCleared()),
78
+ this, SLOT(injectJavascriptHelpers()));
79
+ }
80
+
81
+ void WebPage::injectJavascriptHelpers() {
82
+ QWebFrame* frame = qobject_cast<QWebFrame *>(QObject::sender());
83
+ frame->evaluateJavaScript(m_capybaraJavascript);
84
+ }
85
+
86
+ bool WebPage::shouldInterruptJavaScript() {
87
+ return false;
88
+ }
89
+
90
+ QVariant WebPage::invokeCapybaraFunction(const char *name, QStringList &arguments) {
91
+ QString qname(name);
92
+ QString objectName("CapybaraInvocation");
93
+ JavascriptInvocation invocation(qname, arguments);
94
+ currentFrame()->addToJavaScriptWindowObject(objectName, &invocation);
95
+ QString javascript = QString("Capybara.invoke()");
96
+ return currentFrame()->evaluateJavaScript(javascript);
97
+ }
98
+
99
+ QVariant WebPage::invokeCapybaraFunction(QString &name, QStringList &arguments) {
100
+ return invokeCapybaraFunction(name.toAscii().data(), arguments);
101
+ }
102
+
103
+ void WebPage::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID) {
104
+ QString fullMessage = QString::number(lineNumber) + "|" + message;
105
+ if (!sourceID.isEmpty())
106
+ fullMessage = sourceID + "|" + fullMessage;
107
+ m_consoleMessages.append(fullMessage);
108
+ std::cout << qPrintable(fullMessage) << std::endl;
109
+ }
110
+
111
+ void WebPage::javaScriptAlert(QWebFrame *frame, const QString &message) {
112
+ Q_UNUSED(frame);
113
+ std::cout << "ALERT: " << qPrintable(message) << std::endl;
114
+ }
115
+
116
+ bool WebPage::javaScriptConfirm(QWebFrame *frame, const QString &message) {
117
+ Q_UNUSED(frame);
118
+ Q_UNUSED(message);
119
+ return true;
120
+ }
121
+
122
+ bool WebPage::javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result) {
123
+ Q_UNUSED(frame)
124
+ Q_UNUSED(message)
125
+ Q_UNUSED(defaultValue)
126
+ Q_UNUSED(result)
127
+ return false;
128
+ }
129
+
130
+ void WebPage::loadStarted() {
131
+ m_loading = true;
132
+ }
133
+
134
+ void WebPage::loadFinished(bool success) {
135
+ m_loading = false;
136
+ emit pageFinished(success);
137
+ }
138
+
139
+ bool WebPage::isLoading() const {
140
+ return m_loading;
141
+ }
142
+
143
+ QString WebPage::failureString() {
144
+ return QString("Unable to load URL: ") + currentFrame()->requestedUrl().toString();
145
+ }
146
+
147
+ bool WebPage::render(const QString &fileName) {
148
+ QFileInfo fileInfo(fileName);
149
+ QDir dir;
150
+ dir.mkpath(fileInfo.absolutePath());
151
+
152
+ QSize viewportSize = this->viewportSize();
153
+ QSize pageSize = this->mainFrame()->contentsSize();
154
+ if (pageSize.isEmpty()) {
155
+ return false;
156
+ }
157
+
158
+ QImage buffer(pageSize, QImage::Format_ARGB32);
159
+ buffer.fill(qRgba(255, 255, 255, 0));
160
+
161
+ QPainter p(&buffer);
162
+ p.setRenderHint( QPainter::Antialiasing, true);
163
+ p.setRenderHint( QPainter::TextAntialiasing, true);
164
+ p.setRenderHint( QPainter::SmoothPixmapTransform, true);
165
+
166
+ this->setViewportSize(pageSize);
167
+ this->mainFrame()->render(&p);
168
+ p.end();
169
+ this->setViewportSize(viewportSize);
170
+
171
+ return buffer.save(fileName);
172
+ }
173
+
174
+ QString WebPage::chooseFile(QWebFrame *parentFrame, const QString &suggestedFile) {
175
+ Q_UNUSED(parentFrame);
176
+ Q_UNUSED(suggestedFile);
177
+
178
+ return getLastAttachedFileName();
179
+ }
180
+
181
+ bool WebPage::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) {
182
+ Q_UNUSED(option);
183
+ if (extension == ChooseMultipleFilesExtension) {
184
+ QStringList names = QStringList() << getLastAttachedFileName();
185
+ static_cast<ChooseMultipleFilesExtensionReturn*>(output)->fileNames = names;
186
+ return true;
187
+ }
188
+ return false;
189
+ }
190
+
191
+ QString WebPage::getLastAttachedFileName() {
192
+ return currentFrame()->evaluateJavaScript(QString("Capybara.lastAttachedFile")).toString();
193
+ }
194
+
195
+ void WebPage::replyFinished(QNetworkReply *reply) {
196
+ if (reply->url() == this->currentFrame()->url()) {
197
+ QStringList headers;
198
+ m_lastStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
199
+ QList<QByteArray> list = reply->rawHeaderList();
200
+
201
+ int length = list.size();
202
+ for(int i = 0; i < length; i++) {
203
+ headers << list.at(i)+": "+reply->rawHeader(list.at(i));
204
+ }
205
+
206
+ m_pageHeaders = headers.join("\n");
207
+ }
208
+ }
209
+
210
+ void WebPage::handleSslErrorsForReply(QNetworkReply *reply, const QList<QSslError> &errors) {
211
+ if (m_ignoreSslErrors)
212
+ reply->ignoreSslErrors(errors);
213
+ }
214
+
215
+ void WebPage::ignoreSslErrors() {
216
+ m_ignoreSslErrors = true;
217
+ }
218
+
219
+ int WebPage::getLastStatus() {
220
+ return m_lastStatus;
221
+ }
222
+
223
+ void WebPage::resetResponseHeaders() {
224
+ m_lastStatus = 0;
225
+ m_pageHeaders = QString();
226
+ }
227
+
228
+ void WebPage::resetConsoleMessages() {
229
+ m_consoleMessages.clear();
230
+ }
231
+
232
+ QString WebPage::pageHeaders() {
233
+ return m_pageHeaders;
234
+ }
235
+
236
+ void WebPage::handleUnsupportedContent(QNetworkReply *reply) {
237
+ UnsupportedContentHandler *handler = new UnsupportedContentHandler(this, reply);
238
+ Q_UNUSED(handler);
239
+ }
data/src/WebPage.h ADDED
@@ -0,0 +1,58 @@
1
+ #include <QtWebKit>
2
+ #include <QtNetwork>
3
+
4
+ class WebPage : public QWebPage {
5
+ Q_OBJECT
6
+
7
+ public:
8
+ WebPage(QObject *parent = 0);
9
+ QVariant invokeCapybaraFunction(const char *name, QStringList &arguments);
10
+ QVariant invokeCapybaraFunction(QString &name, QStringList &arguments);
11
+ QString failureString();
12
+ QString userAgentForUrl(const QUrl &url ) const;
13
+ void setUserAgent(QString userAgent);
14
+ int getLastStatus();
15
+ void resetResponseHeaders();
16
+ void setCustomNetworkAccessManager();
17
+ bool render(const QString &fileName);
18
+ virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
19
+ void ignoreSslErrors();
20
+ QString consoleMessages();
21
+ void resetConsoleMessages();
22
+ void resetWindowSize();
23
+
24
+ public slots:
25
+ bool shouldInterruptJavaScript();
26
+ void injectJavascriptHelpers();
27
+ void loadStarted();
28
+ void loadFinished(bool);
29
+ bool isLoading() const;
30
+ QString pageHeaders();
31
+ void frameCreated(QWebFrame *);
32
+ void replyFinished(QNetworkReply *reply);
33
+ void handleSslErrorsForReply(QNetworkReply *reply, const QList<QSslError> &);
34
+ void handleUnsupportedContent(QNetworkReply *reply);
35
+
36
+ signals:
37
+ void pageFinished(bool);
38
+
39
+ protected:
40
+ virtual void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID);
41
+ virtual void javaScriptAlert(QWebFrame *frame, const QString &message);
42
+ virtual bool javaScriptConfirm(QWebFrame *frame, const QString &message);
43
+ virtual bool javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result);
44
+ virtual QString chooseFile(QWebFrame * parentFrame, const QString &suggestedFile);
45
+
46
+ private:
47
+ QString m_capybaraJavascript;
48
+ QString m_userAgent;
49
+ bool m_loading;
50
+ QString getLastAttachedFileName();
51
+ void loadJavascript();
52
+ void setUserStylesheet();
53
+ int m_lastStatus;
54
+ QString m_pageHeaders;
55
+ bool m_ignoreSslErrors;
56
+ QStringList m_consoleMessages;
57
+ };
58
+
data/src/body.cpp ADDED
@@ -0,0 +1,10 @@
1
+ #include "Body.h"
2
+ #include "WebPage.h"
3
+
4
+ Body::Body(WebPage *page, QStringList &arguments, QObject *parent) : Command(page, arguments, parent) {
5
+ }
6
+
7
+ void Body::start() {
8
+ QString result = page()->currentFrame()->toHtml();
9
+ emit finished(new Response(true, result));
10
+ }
data/src/capybara.js ADDED
@@ -0,0 +1,315 @@
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
+ path: function(index) {
60
+ return "/" + this.getXPathNode(this.nodes[index]).join("/");
61
+ },
62
+
63
+ getXPathNode: function(node, path) {
64
+ path = path || [];
65
+ if (node.parentNode) {
66
+ path = this.getXPathNode(node.parentNode, path);
67
+ }
68
+
69
+ var first = node;
70
+ while (first.previousSibling)
71
+ first = first.previousSibling;
72
+
73
+ var count = 0;
74
+ var index = 0;
75
+ var iter = first;
76
+ while (iter) {
77
+ if (iter.nodeType == 1 && iter.nodeName == node.nodeName)
78
+ count++;
79
+ if (iter.isSameNode(node))
80
+ index = count;
81
+ iter = iter.nextSibling;
82
+ continue;
83
+ }
84
+
85
+ if (node.nodeType == 1)
86
+ path.push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 1 ? "["+index+"]" : ''));
87
+
88
+ return path;
89
+ },
90
+
91
+ tagName: function(index) {
92
+ return this.nodes[index].tagName.toLowerCase();
93
+ },
94
+
95
+ submit: function(index) {
96
+ return this.nodes[index].submit();
97
+ },
98
+
99
+ mousedown: function(index) {
100
+ var mousedownEvent = document.createEvent('MouseEvents');
101
+ mousedownEvent.initMouseEvent('mousedown', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
102
+ this.nodes[index].dispatchEvent(mousedownEvent);
103
+ },
104
+
105
+ mouseup: function(index) {
106
+ var mouseupEvent = document.createEvent('MouseEvents');
107
+ mouseupEvent.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
108
+ this.nodes[index].dispatchEvent(mouseupEvent);
109
+ },
110
+
111
+ click: function (index) {
112
+ this.mousedown(index);
113
+ this.mouseup(index);
114
+ var clickEvent = document.createEvent('MouseEvents');
115
+ clickEvent.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
116
+ this.nodes[index].dispatchEvent(clickEvent);
117
+ },
118
+
119
+ trigger: function (index, eventName) {
120
+ var eventObject = document.createEvent("HTMLEvents");
121
+ eventObject.initEvent(eventName, true, true);
122
+ this.nodes[index].dispatchEvent(eventObject);
123
+ },
124
+
125
+ keypress: function(index, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
126
+ var eventObject = document.createEvent("Events");
127
+ eventObject.initEvent('keypress', true, true);
128
+ eventObject.window = window;
129
+ eventObject.altKey = altKey;
130
+ eventObject.ctrlKey = ctrlKey;
131
+ eventObject.shiftKey = shiftKey;
132
+ eventObject.metaKey = metaKey;
133
+ eventObject.keyCode = keyCode;
134
+ eventObject.charCode = charCode;
135
+ eventObject.which = keyCode;
136
+ this.nodes[index].dispatchEvent(eventObject);
137
+ },
138
+
139
+ keyupdown: function(index, eventName, keyCode) {
140
+ var eventObject = document.createEvent("HTMLEvents");
141
+ eventObject.initEvent(eventName, true, true);
142
+ eventObject.keyCode = keyCode;
143
+ eventObject.which = keyCode;
144
+ eventObject.charCode = 0;
145
+ this.nodes[index].dispatchEvent(eventObject);
146
+ },
147
+
148
+ visible: function (index) {
149
+ var element = this.nodes[index];
150
+ while (element) {
151
+ if (element.ownerDocument.defaultView.getComputedStyle(element, null).getPropertyValue("display") == 'none')
152
+ return false;
153
+ element = element.parentElement;
154
+ }
155
+ return true;
156
+ },
157
+
158
+ selected: function (index) {
159
+ return this.nodes[index].selected;
160
+ },
161
+
162
+ value: function(index) {
163
+ return this.nodes[index].value;
164
+ },
165
+
166
+ characterToKeyCode: function(character) {
167
+ var code = character.toUpperCase().charCodeAt(0);
168
+ var specialKeys = {
169
+ 96: 192, //`
170
+ 45: 189, //-
171
+ 61: 187, //=
172
+ 91: 219, //[
173
+ 93: 221, //]
174
+ 92: 220, //\
175
+ 59: 186, //;
176
+ 39: 222, //'
177
+ 44: 188, //,
178
+ 46: 190, //.
179
+ 47: 191, ///
180
+ 127: 46, //delete
181
+ 126: 192, //~
182
+ 33: 49, //!
183
+ 64: 50, //@
184
+ 35: 51, //#
185
+ 36: 52, //$
186
+ 37: 53, //%
187
+ 94: 54, //^
188
+ 38: 55, //&
189
+ 42: 56, //*
190
+ 40: 57, //(
191
+ 41: 48, //)
192
+ 95: 189, //_
193
+ 43: 187, //+
194
+ 123: 219, //{
195
+ 125: 221, //}
196
+ 124: 220, //|
197
+ 58: 186, //:
198
+ 34: 222, //"
199
+ 60: 188, //<
200
+ 62: 190, //>
201
+ 63: 191 //?
202
+ };
203
+ if (specialKeys[code]) {
204
+ code = specialKeys[code];
205
+ }
206
+ return code;
207
+ },
208
+
209
+ set: function (index, value) {
210
+ var length, maxLength, node, strindex, textTypes, type;
211
+
212
+ node = this.nodes[index];
213
+ type = (node.type || node.tagName).toLowerCase();
214
+ textTypes = ["email", "number", "password", "search", "tel", "text", "textarea", "url"];
215
+
216
+ if (textTypes.indexOf(type) != -1) {
217
+ this.trigger(index, "focus");
218
+
219
+ maxLength = this.attribute(index, "maxlength");
220
+ if (maxLength && value.length > maxLength) {
221
+ length = maxLength;
222
+ } else {
223
+ length = value.length;
224
+ }
225
+
226
+ node.value = "";
227
+ for (strindex = 0; strindex < length; strindex++) {
228
+ node.value += value[strindex];
229
+ var keyCode = this.characterToKeyCode(value[strindex]);
230
+ this.keyupdown(index, "keydown", keyCode);
231
+ this.keypress(index, false, false, false, false, value.charCodeAt(strindex), value.charCodeAt(strindex));
232
+ this.keyupdown(index, "keyup", keyCode);
233
+ this.trigger(index, "input");
234
+ }
235
+ this.trigger(index, "change");
236
+ this.trigger(index, "blur");
237
+
238
+ } else if (type === "checkbox" || type === "radio") {
239
+ if (node.checked != (value === "true")) {
240
+ this.click(index)
241
+ }
242
+
243
+ } else if (type === "file") {
244
+ this.lastAttachedFile = value;
245
+ this.click(index)
246
+
247
+ } else {
248
+ node.value = value;
249
+ }
250
+ },
251
+
252
+ selectOption: function(index) {
253
+ this.nodes[index].selected = true;
254
+ this.trigger(index, "change");
255
+ },
256
+
257
+ unselectOption: function(index) {
258
+ this.nodes[index].selected = false;
259
+ this.trigger(index, "change");
260
+ },
261
+
262
+ centerPostion: function(element) {
263
+ this.reflow(element);
264
+ var rect = element.getBoundingClientRect();
265
+ var position = {
266
+ x: rect.width / 2,
267
+ y: rect.height / 2
268
+ };
269
+ do {
270
+ position.x += element.offsetLeft;
271
+ position.y += element.offsetTop;
272
+ } while ((element = element.offsetParent));
273
+ position.x = Math.floor(position.x), position.y = Math.floor(position.y);
274
+
275
+ return position;
276
+ },
277
+
278
+ reflow: function(element, force) {
279
+ if (force || element.offsetWidth === 0) {
280
+ var prop, oldStyle = {}, newStyle = {position: "absolute", visibility : "hidden", display: "block" };
281
+ for (prop in newStyle) {
282
+ oldStyle[prop] = element.style[prop];
283
+ element.style[prop] = newStyle[prop];
284
+ }
285
+ element.offsetWidth, element.offsetHeight; // force reflow
286
+ for (prop in oldStyle)
287
+ element.style[prop] = oldStyle[prop];
288
+ }
289
+ },
290
+
291
+ dragTo: function (index, targetIndex) {
292
+ var element = this.nodes[index], target = this.nodes[targetIndex];
293
+ var position = this.centerPostion(element);
294
+ var options = {
295
+ clientX: position.x,
296
+ clientY: position.y
297
+ };
298
+ var mouseTrigger = function(eventName, options) {
299
+ var eventObject = document.createEvent("MouseEvents");
300
+ eventObject.initMouseEvent(eventName, true, true, window, 0, 0, 0, options.clientX || 0, options.clientY || 0, false, false, false, false, 0, null);
301
+ element.dispatchEvent(eventObject);
302
+ }
303
+ mouseTrigger('mousedown', options);
304
+ options.clientX += 1, options.clientY += 1;
305
+ mouseTrigger('mousemove', options);
306
+
307
+ position = this.centerPostion(target), options = {
308
+ clientX: position.x,
309
+ clientY: position.y
310
+ };
311
+ mouseTrigger('mousemove', options);
312
+ mouseTrigger('mouseup', options);
313
+ }
314
+ };
315
+
@@ -0,0 +1,29 @@
1
+ #define CHECK_COMMAND(expectedName) \
2
+ if (strcmp(#expectedName, name) == 0) { \
3
+ return new expectedName(m_page, arguments, 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)
25
+ CHECK_COMMAND(ConsoleMessages)
26
+ CHECK_COMMAND(RequestedUrl)
27
+ CHECK_COMMAND(CurrentUrl)
28
+ CHECK_COMMAND(ResizeWindow)
29
+ CHECK_COMMAND(IgnoreSslErrors)
data/src/main.cpp ADDED
@@ -0,0 +1,33 @@
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
+
23
+ Server server(0);
24
+
25
+ if (server.start()) {
26
+ std::cout << "Capybara-webkit server started, listening on port: " << server.server_port() << std::endl;
27
+ return app.exec();
28
+ } else {
29
+ std::cerr << "Couldn't start capybara-webkit server" << std::endl;
30
+ return 1;
31
+ }
32
+ }
33
+