capybara-webkit 0.14.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/.gitignore +2 -0
  2. data/.travis.yml +21 -0
  3. data/Appraisals +4 -4
  4. data/CONTRIBUTING.md +14 -3
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +27 -19
  7. data/NEWS.md +15 -0
  8. data/README.md +126 -76
  9. data/Vagrantfile +7 -0
  10. data/capybara-webkit.gemspec +3 -0
  11. data/gemfiles/2.0.gemfile +7 -0
  12. data/gemfiles/2.0.gemfile.lock +72 -0
  13. data/gemfiles/2.1.gemfile +7 -0
  14. data/gemfiles/2.1.gemfile.lock +71 -0
  15. data/lib/capybara/webkit/browser.rb +22 -22
  16. data/lib/capybara/webkit/connection.rb +9 -6
  17. data/lib/capybara/webkit/driver.rb +22 -6
  18. data/lib/capybara/webkit/errors.rb +25 -0
  19. data/lib/capybara/webkit/node.rb +36 -10
  20. data/lib/capybara/webkit/version.rb +1 -1
  21. data/spec/browser_spec.rb +16 -1
  22. data/spec/capybara_webkit_builder_spec.rb +9 -3
  23. data/spec/connection_spec.rb +19 -3
  24. data/spec/driver_spec.rb +324 -144
  25. data/spec/errors_spec.rb +11 -0
  26. data/spec/integration/session_spec.rb +244 -0
  27. data/spec/selenium_compatibility_spec.rb +3 -1
  28. data/spec/spec_helper.rb +1 -9
  29. data/src/Authenticate.cpp +3 -2
  30. data/src/ClearCookies.cpp +1 -1
  31. data/src/ClearPromptText.cpp +1 -1
  32. data/src/Command.cpp +8 -4
  33. data/src/Command.h +7 -4
  34. data/src/CommandFactory.cpp +4 -2
  35. data/src/CommandParser.cpp +1 -1
  36. data/src/Connection.cpp +4 -4
  37. data/src/ConsoleMessages.cpp +1 -1
  38. data/src/CurrentUrl.cpp +2 -2
  39. data/src/EnableLogging.cpp +1 -1
  40. data/src/ErrorMessage.cpp +26 -0
  41. data/src/ErrorMessage.h +21 -0
  42. data/src/Evaluate.cpp +1 -1
  43. data/src/Execute.cpp +3 -2
  44. data/src/FindCss.cpp +13 -0
  45. data/src/FindCss.h +11 -0
  46. data/src/FindXpath.cpp +13 -0
  47. data/src/FindXpath.h +11 -0
  48. data/src/FrameFocus.cpp +4 -3
  49. data/src/GetCookies.cpp +1 -1
  50. data/src/GetTimeout.cpp +1 -1
  51. data/src/GetWindowHandle.cpp +1 -1
  52. data/src/GetWindowHandles.cpp +1 -1
  53. data/src/Header.cpp +2 -2
  54. data/src/Headers.cpp +1 -6
  55. data/src/IgnoreSslErrors.cpp +1 -1
  56. data/src/InvocationResult.cpp +29 -0
  57. data/src/InvocationResult.h +16 -0
  58. data/src/JavascriptAlertMessages.cpp +1 -1
  59. data/src/JavascriptCommand.cpp +15 -0
  60. data/src/JavascriptCommand.h +20 -0
  61. data/src/JavascriptConfirmMessages.cpp +1 -1
  62. data/src/JavascriptInvocation.cpp +128 -1
  63. data/src/JavascriptInvocation.h +22 -1
  64. data/src/JavascriptPromptMessages.cpp +1 -1
  65. data/src/NetworkAccessManager.cpp +8 -16
  66. data/src/NetworkAccessManager.h +5 -11
  67. data/src/NetworkReplyProxy.cpp +91 -0
  68. data/src/NetworkReplyProxy.h +65 -0
  69. data/src/Node.cpp +4 -4
  70. data/src/Node.h +2 -2
  71. data/src/NullCommand.cpp +2 -1
  72. data/src/PageLoadingCommand.cpp +2 -1
  73. data/src/Render.cpp +1 -1
  74. data/src/Reset.cpp +1 -1
  75. data/src/ResizeWindow.cpp +1 -1
  76. data/src/Response.cpp +7 -0
  77. data/src/Response.h +8 -3
  78. data/src/SetConfirmAction.cpp +1 -1
  79. data/src/SetCookie.cpp +2 -2
  80. data/src/SetPromptAction.cpp +1 -1
  81. data/src/SetPromptText.cpp +1 -1
  82. data/src/SetProxy.cpp +2 -2
  83. data/src/SetSkipImageLoading.cpp +1 -1
  84. data/src/SetTimeout.cpp +3 -2
  85. data/src/SetUrlBlacklist.cpp +2 -2
  86. data/src/Status.cpp +1 -1
  87. data/src/TimeoutCommand.cpp +4 -2
  88. data/src/Title.cpp +11 -0
  89. data/src/Title.h +9 -0
  90. data/src/Version.cpp +13 -0
  91. data/src/Version.h +10 -0
  92. data/src/Visit.cpp +1 -1
  93. data/src/WebPage.cpp +49 -27
  94. data/src/WebPage.h +14 -7
  95. data/src/WebPageManager.cpp +10 -1
  96. data/src/WebPageManager.h +4 -1
  97. data/src/WindowFocus.cpp +3 -2
  98. data/src/body.cpp +3 -6
  99. data/src/capybara.js +103 -101
  100. data/src/find_command.h +4 -2
  101. data/src/main.cpp +1 -1
  102. data/src/stable.h +39 -0
  103. data/src/webkit_server.pro +26 -6
  104. data/vagrant_setup.sh +58 -0
  105. metadata +51 -78
  106. data/gemfiles/1.0.gemfile +0 -7
  107. data/gemfiles/1.0.gemfile.lock +0 -70
  108. data/gemfiles/1.1.gemfile +0 -7
  109. data/gemfiles/1.1.gemfile.lock +0 -70
  110. data/src/Find.cpp +0 -20
  111. data/src/Find.h +0 -11
data/src/WindowFocus.cpp CHANGED
@@ -3,6 +3,7 @@
3
3
  #include "WebPage.h"
4
4
  #include "CommandFactory.h"
5
5
  #include "WebPageManager.h"
6
+ #include "ErrorMessage.h"
6
7
 
7
8
  WindowFocus::WindowFocus(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
8
9
  }
@@ -12,12 +13,12 @@ void WindowFocus::start() {
12
13
  }
13
14
 
14
15
  void WindowFocus::windowNotFound() {
15
- emitFinished(false, QString("Unable to locate window. "));
16
+ finish(false, new ErrorMessage("Unable to locate window."));
16
17
  }
17
18
 
18
19
  void WindowFocus::success(WebPage *page) {
19
20
  page->setFocus();
20
- emitFinished(true);
21
+ finish(true);
21
22
  }
22
23
 
23
24
  void WindowFocus::focusWindow(QString selector) {
data/src/body.cpp CHANGED
@@ -6,11 +6,8 @@ Body::Body(WebPageManager *manager, QStringList &arguments, QObject *parent) : S
6
6
  }
7
7
 
8
8
  void Body::start() {
9
- QString result;
10
- if (page()->unsupportedContentLoaded())
11
- result = page()->currentFrame()->toPlainText();
9
+ if (page()->contentType().contains("html"))
10
+ finish(true, page()->currentFrame()->toHtml());
12
11
  else
13
- result = page()->currentFrame()->toHtml();
14
-
15
- emitFinished(true, result);
12
+ finish(true, page()->body());
16
13
  }
data/src/capybara.js CHANGED
@@ -4,22 +4,30 @@ Capybara = {
4
4
  attachedFiles: [],
5
5
 
6
6
  invoke: function () {
7
- return this[CapybaraInvocation.functionName].apply(this, CapybaraInvocation.arguments);
7
+ try {
8
+ return this[CapybaraInvocation.functionName].apply(this, CapybaraInvocation.arguments);
9
+ } catch (e) {
10
+ CapybaraInvocation.error = e;
11
+ }
12
+ },
13
+
14
+ findXpath: function (xpath) {
15
+ return this.findXpathRelativeTo(document, xpath);
8
16
  },
9
17
 
10
- find: function (xpath) {
11
- return this.findRelativeTo(document, xpath);
18
+ findCss: function (selector) {
19
+ return this.findCssRelativeTo(document, selector);
12
20
  },
13
21
 
14
- currentUrl: function () {
15
- return window.location.toString();
22
+ findXpathWithin: function (index, xpath) {
23
+ return this.findXpathRelativeTo(this.nodes[index], xpath);
16
24
  },
17
25
 
18
- findWithin: function (index, xpath) {
19
- return this.findRelativeTo(this.nodes[index], xpath);
26
+ findCssWithin: function (index, selector) {
27
+ return this.findCssRelativeTo(this.nodes[index], selector);
20
28
  },
21
29
 
22
- findRelativeTo: function (reference, xpath) {
30
+ findXpathRelativeTo: function (reference, xpath) {
23
31
  var iterator = document.evaluate(xpath, reference, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
24
32
  var node;
25
33
  var results = [];
@@ -31,8 +39,20 @@ Capybara = {
31
39
  return results.join(",");
32
40
  },
33
41
 
42
+ findCssRelativeTo: function (reference, selector) {
43
+ var elements = reference.querySelectorAll(selector);
44
+ var results = [];
45
+ for (var i = 0; i < elements.length; i++) {
46
+ this.nextIndex++;
47
+ this.nodes[this.nextIndex] = elements[i];
48
+ results.push(this.nextIndex);
49
+ }
50
+ return results.join(",");
51
+ },
52
+
34
53
  isAttached: function(index) {
35
- return document.evaluate("ancestor-or-self::html", this.nodes[index], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
54
+ return this.nodes[index] &&
55
+ document.evaluate("ancestor-or-self::html", this.nodes[index], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
36
56
  },
37
57
 
38
58
  text: function (index) {
@@ -41,10 +61,15 @@ Capybara = {
41
61
  if (type == "textarea") {
42
62
  return node.innerHTML;
43
63
  } else {
44
- return node.innerText;
64
+ return node.innerText || node.textContent;
45
65
  }
46
66
  },
47
67
 
68
+ allText: function (index) {
69
+ var node = this.nodes[index];
70
+ return node.textContent;
71
+ },
72
+
48
73
  attribute: function (index, name) {
49
74
  switch(name) {
50
75
  case 'checked':
@@ -108,53 +133,67 @@ Capybara = {
108
133
  return this.nodes[index].submit();
109
134
  },
110
135
 
111
- mousedown: function(index) {
112
- var mousedownEvent = document.createEvent('MouseEvents');
113
- mousedownEvent.initMouseEvent('mousedown', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
114
- this.nodes[index].dispatchEvent(mousedownEvent);
136
+ clickTest: function(node, pos) {
137
+ var el = document.elementFromPoint(pos.relativeX, pos.relativeY);
138
+
139
+ while (el) {
140
+ if (el === node)
141
+ return CapybaraInvocation.clickTest(node, pos.absoluteX, pos.absoluteY);
142
+ else
143
+ el = el.parentNode;
144
+ }
145
+
146
+ return false;
115
147
  },
116
148
 
117
- mouseup: function(index) {
118
- var mouseupEvent = document.createEvent('MouseEvents');
119
- mouseupEvent.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
120
- this.nodes[index].dispatchEvent(mouseupEvent);
149
+ clickPosition: function(node) {
150
+ var rects = node.getClientRects();
151
+ var rect;
152
+
153
+ for (var i = 0; i < rects.length; i++) {
154
+ rect = rects[i];
155
+ if (rect.width > 0 && rect.height > 0)
156
+ return CapybaraInvocation.clickPosition(node, rect.left, rect.top, rect.width, rect.height);
157
+ }
121
158
  },
122
159
 
123
- click: function (index) {
124
- this.mousedown(index);
125
- this.focus(index);
126
- this.mouseup(index);
127
- var clickEvent = document.createEvent('MouseEvents');
128
- clickEvent.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
129
- this.nodes[index].dispatchEvent(clickEvent);
160
+ click: function (index, action) {
161
+ var node = this.nodes[index];
162
+ node.scrollIntoViewIfNeeded();
163
+
164
+ var pos = this.clickPosition(node);
165
+
166
+ if (pos && this.clickTest(node, pos))
167
+ action(pos.absoluteX, pos.absoluteY);
168
+ else
169
+ throw new Capybara.ClickFailed(this.path(index), pos);
130
170
  },
131
171
 
132
- trigger: function (index, eventName) {
133
- var eventObject = document.createEvent("HTMLEvents");
134
- eventObject.initEvent(eventName, true, true);
135
- this.nodes[index].dispatchEvent(eventObject);
172
+ leftClick: function (index) {
173
+ this.click(index, CapybaraInvocation.leftClick);
136
174
  },
137
175
 
138
- keypress: function(index, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
139
- var eventObject = document.createEvent("Events");
140
- eventObject.initEvent('keypress', true, true);
141
- eventObject.window = window;
142
- eventObject.altKey = altKey;
143
- eventObject.ctrlKey = ctrlKey;
144
- eventObject.shiftKey = shiftKey;
145
- eventObject.metaKey = metaKey;
146
- eventObject.keyCode = keyCode;
147
- eventObject.charCode = charCode;
148
- eventObject.which = keyCode;
149
- this.nodes[index].dispatchEvent(eventObject);
176
+ doubleClick: function(index) {
177
+ this.click(index, CapybaraInvocation.leftClick);
178
+ this.click(index, CapybaraInvocation.doubleClick);
179
+ },
180
+
181
+ rightClick: function(index) {
182
+ this.click(index, CapybaraInvocation.rightClick);
150
183
  },
151
184
 
152
- keyupdown: function(index, eventName, keyCode) {
185
+ hover: function (index) {
186
+ var node = this.nodes[index];
187
+ node.scrollIntoViewIfNeeded();
188
+
189
+ var pos = this.clickPosition(node);
190
+ if (pos)
191
+ CapybaraInvocation.hover(pos.absoluteX, pos.absoluteY);
192
+ },
193
+
194
+ trigger: function (index, eventName) {
153
195
  var eventObject = document.createEvent("HTMLEvents");
154
196
  eventObject.initEvent(eventName, true, true);
155
- eventObject.keyCode = keyCode;
156
- eventObject.which = keyCode;
157
- eventObject.charCode = 0;
158
197
  this.nodes[index].dispatchEvent(eventObject);
159
198
  },
160
199
 
@@ -187,49 +226,6 @@ Capybara = {
187
226
  return true;
188
227
  },
189
228
 
190
- characterToKeyCode: function(character) {
191
- var code = character.toUpperCase().charCodeAt(0);
192
- var specialKeys = {
193
- 96: 192, //`
194
- 45: 189, //-
195
- 61: 187, //=
196
- 91: 219, //[
197
- 93: 221, //]
198
- 92: 220, //\
199
- 59: 186, //;
200
- 39: 222, //'
201
- 44: 188, //,
202
- 46: 190, //.
203
- 47: 191, ///
204
- 127: 46, //delete
205
- 126: 192, //~
206
- 33: 49, //!
207
- 64: 50, //@
208
- 35: 51, //#
209
- 36: 52, //$
210
- 37: 53, //%
211
- 94: 54, //^
212
- 38: 55, //&
213
- 42: 56, //*
214
- 40: 57, //(
215
- 41: 48, //)
216
- 95: 189, //_
217
- 43: 187, //+
218
- 123: 219, //{
219
- 125: 221, //}
220
- 124: 220, //|
221
- 58: 186, //:
222
- 34: 222, //"
223
- 60: 188, //<
224
- 62: 190, //>
225
- 63: 191 //?
226
- };
227
- if (specialKeys[code]) {
228
- code = specialKeys[code];
229
- }
230
- return code;
231
- },
232
-
233
229
  set: function (index, value) {
234
230
  var length, maxLength, node, strindex, textTypes, type;
235
231
 
@@ -247,25 +243,21 @@ Capybara = {
247
243
  length = value.length;
248
244
  }
249
245
 
250
- node.value = "";
246
+ if (!node.readOnly)
247
+ node.value = "";
248
+
251
249
  for (strindex = 0; strindex < length; strindex++) {
252
- node.value += value[strindex];
253
- var keyCode = this.characterToKeyCode(value[strindex]);
254
- this.keyupdown(index, "keydown", keyCode);
255
- this.keypress(index, false, false, false, false, value.charCodeAt(strindex), value.charCodeAt(strindex));
256
- this.keyupdown(index, "keyup", keyCode);
257
- this.trigger(index, "input");
250
+ CapybaraInvocation.keypress(value[strindex]);
258
251
  }
259
- this.trigger(index, "change");
260
252
 
261
253
  } else if (type === "checkbox" || type === "radio") {
262
254
  if (node.checked != (value === "true")) {
263
- this.click(index);
255
+ this.leftClick(index);
264
256
  }
265
257
 
266
258
  } else if (type === "file") {
267
259
  this.attachedFiles = Array.prototype.slice.call(arguments, 1);
268
- this.click(index);
260
+ this.leftClick(index);
269
261
 
270
262
  } else {
271
263
  node.value = value;
@@ -286,7 +278,7 @@ Capybara = {
286
278
  this.trigger(index, "change");
287
279
  },
288
280
 
289
- centerPostion: function(element) {
281
+ centerPosition: function(element) {
290
282
  this.reflow(element);
291
283
  var rect = element.getBoundingClientRect();
292
284
  var position = {
@@ -320,7 +312,7 @@ Capybara = {
320
312
 
321
313
  dragTo: function (index, targetIndex) {
322
314
  var element = this.nodes[index], target = this.nodes[targetIndex];
323
- var position = this.centerPostion(element);
315
+ var position = this.centerPosition(element);
324
316
  var options = {
325
317
  clientX: position.x,
326
318
  clientY: position.y
@@ -335,7 +327,7 @@ Capybara = {
335
327
  options.clientY += 1;
336
328
  mouseTrigger('mousemove', options);
337
329
 
338
- position = this.centerPostion(target);
330
+ position = this.centerPosition(target);
339
331
  options = {
340
332
  clientX: position.x,
341
333
  clientY: position.y
@@ -349,3 +341,13 @@ Capybara = {
349
341
  }
350
342
  };
351
343
 
344
+ Capybara.ClickFailed = function(path, position) {
345
+ this.name = 'Capybara.ClickFailed';
346
+ this.message = 'Failed to click element ' + path;
347
+ if (position)
348
+ this.message += ' at position ' + position["absoluteX"] + ', ' + position["absoluteY"];
349
+ else
350
+ this.message += ' at unknown position';
351
+ };
352
+ Capybara.ClickFailed.prototype = new Error();
353
+ Capybara.ClickFailed.prototype.constructor = Capybara.ClickFailed;
data/src/find_command.h CHANGED
@@ -4,7 +4,7 @@
4
4
  }
5
5
 
6
6
  CHECK_COMMAND(Visit)
7
- CHECK_COMMAND(Find)
7
+ CHECK_COMMAND(FindXpath)
8
8
  CHECK_COMMAND(Reset)
9
9
  CHECK_COMMAND(Node)
10
10
  CHECK_COMMAND(Evaluate)
@@ -39,4 +39,6 @@ CHECK_COMMAND(JavascriptPromptMessages)
39
39
  CHECK_COMMAND(GetTimeout)
40
40
  CHECK_COMMAND(SetTimeout)
41
41
  CHECK_COMMAND(SetUrlBlacklist)
42
-
42
+ CHECK_COMMAND(Title)
43
+ CHECK_COMMAND(Version)
44
+ CHECK_COMMAND(FindCss)
data/src/main.cpp CHANGED
@@ -1,5 +1,5 @@
1
1
  #include "Server.h"
2
- #include <QtGui>
2
+ #include <QApplication>
3
3
  #include <iostream>
4
4
  #ifdef Q_OS_UNIX
5
5
  #include <unistd.h>
data/src/stable.h ADDED
@@ -0,0 +1,39 @@
1
+ #include <QApplication>
2
+ #include <QByteArray>
3
+ #include <QDebug>
4
+ #include <QEvent>
5
+ #include <QFile>
6
+ #include <QIODevice>
7
+ #include <QList>
8
+ #include <QNetworkCookie>
9
+ #include <QNetworkProxy>
10
+ #include <QNetworkReply>
11
+ #include <QNetworkRequest>
12
+ #include <QObject>
13
+ #include <QResource>
14
+ #include <QSet>
15
+ #include <QString>
16
+ #include <QStringList>
17
+ #include <QTcpServer>
18
+ #include <QTcpSocket>
19
+ #include <QTimer>
20
+ #include <QUuid>
21
+ #include <QVariant>
22
+ #include <QVariantList>
23
+ #include <QWebElement>
24
+ #include <QWebSettings>
25
+ #include <QtNetwork/QNetworkAccessManager>
26
+ #include <QtNetwork/QNetworkCookie>
27
+ #include <QtNetwork/QNetworkCookieJar>
28
+ #include <QtNetwork/QNetworkReply>
29
+ #include <QtNetwork/QNetworkRequest>
30
+ #include <QtNetwork>
31
+ #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
32
+ #include <QtWebKitWidgets>
33
+ #else
34
+ #include <QtWebKit>
35
+ #endif
36
+ #include <cmath>
37
+ #include <fstream>
38
+ #include <iostream>
39
+ #include <sstream>
@@ -2,6 +2,7 @@ TEMPLATE = app
2
2
  TARGET = webkit_server
3
3
  DESTDIR = .
4
4
  HEADERS = \
5
+ Version.h \
5
6
  EnableLogging.h \
6
7
  Authenticate.h \
7
8
  SetConfirmAction.h \
@@ -21,7 +22,6 @@ HEADERS = \
21
22
  Command.h \
22
23
  SocketCommand.h \
23
24
  Visit.h \
24
- Find.h \
25
25
  Reset.h \
26
26
  Node.h \
27
27
  JavascriptInvocation.h \
@@ -55,9 +55,17 @@ HEADERS = \
55
55
  TimeoutCommand.h \
56
56
  SetUrlBlacklist.h \
57
57
  NoOpReply.h \
58
- JsonSerializer.h
58
+ JsonSerializer.h \
59
+ InvocationResult.h \
60
+ ErrorMessage.h \
61
+ Title.h \
62
+ FindCss.h \
63
+ JavascriptCommand.h \
64
+ FindXpath.h \
65
+ NetworkReplyProxy.h
59
66
 
60
67
  SOURCES = \
68
+ Version.cpp \
61
69
  EnableLogging.cpp \
62
70
  Authenticate.cpp \
63
71
  SetConfirmAction.cpp \
@@ -78,7 +86,6 @@ SOURCES = \
78
86
  Command.cpp \
79
87
  SocketCommand.cpp \
80
88
  Visit.cpp \
81
- Find.cpp \
82
89
  Reset.cpp \
83
90
  Node.cpp \
84
91
  JavascriptInvocation.cpp \
@@ -112,10 +119,23 @@ SOURCES = \
112
119
  TimeoutCommand.cpp \
113
120
  SetUrlBlacklist.cpp \
114
121
  NoOpReply.cpp \
115
- JsonSerializer.cpp
122
+ JsonSerializer.cpp \
123
+ InvocationResult.cpp \
124
+ ErrorMessage.cpp \
125
+ Title.cpp \
126
+ FindCss.cpp \
127
+ JavascriptCommand.cpp \
128
+ FindXpath.cpp \
129
+ NetworkReplyProxy.cpp
116
130
 
117
131
  RESOURCES = webkit_server.qrc
118
- QT += network webkit
119
- CONFIG += console
132
+ QT += network
133
+ greaterThan(QT_MAJOR_VERSION, 4) {
134
+ QT += webkitwidgets
135
+ } else {
136
+ QT += webkit
137
+ }
138
+ CONFIG += console precompile_header
120
139
  CONFIG -= app_bundle
140
+ PRECOMPILED_HEADER = stable.h
121
141