capybara-webkit 1.14.0 → 1.15.0

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.
@@ -2,7 +2,8 @@ require "spec_helper"
2
2
  require "capybara/webkit/server"
3
3
 
4
4
  describe Capybara::Webkit::Connection do
5
- it "ensures the process ends", skip_on_windows: true, skip_on_jruby: true do
5
+ it "ensures the process ends when the parent process ends", skip_on_windows: true, skip_on_jruby: true do
6
+ sleep 1 # Without this sleep popen3 hangs on OSX when running the tests - not really sure why
6
7
  read_io, write_io = IO.pipe
7
8
 
8
9
  fork_pid = fork do
@@ -25,6 +26,12 @@ describe Capybara::Webkit::Connection do
25
26
  end
26
27
  end
27
28
 
29
+ it "shouldn't output extraneous warnings when exiting", skip_on_windows: true do
30
+ output_str, status = Open3.capture2e("rspec spec/fixtures/exit_text.rb")
31
+ expect(status.exitstatus).to eq(0)
32
+ expect(output_str).not_to include("AsynchronousCloseException")
33
+ end
34
+
28
35
  it "raises an error if the server has stopped", skip_on_windows: true do
29
36
  path = "false"
30
37
  stub_const("Capybara::Webkit::Server::SERVER_PATH", path)
@@ -82,7 +89,7 @@ describe Capybara::Webkit::Connection do
82
89
  socket.puts script.to_s.bytesize
83
90
  socket.print script
84
91
 
85
- expect(read_io).to include_response "\nhello world"
92
+ expect(read_io).to include_response /\n\d{2}:\d{2}:\d{2}\.\d{3} hello world/
86
93
  end
87
94
 
88
95
  it "does not forward stderr to nil" do
@@ -13,7 +13,7 @@ $webkit_server = Capybara::Webkit::Server.new
13
13
  $webkit_connection = Capybara::Webkit::Connection.new(server: $webkit_server)
14
14
  $webkit_browser = Capybara::Webkit::Browser.new($webkit_connection)
15
15
 
16
- if ENV['DEBUG']
16
+ if ENV["DEBUG"]
17
17
  $webkit_browser.enable_logging
18
18
  end
19
19
 
@@ -61,6 +61,9 @@ RSpec.configure do |c|
61
61
  end
62
62
  )
63
63
  }
64
+
65
+ c.filter_run :focus unless ENV["TRAVIS"]
66
+ c.run_all_when_everything_filtered = true
64
67
  end
65
68
 
66
69
  def with_env_vars(vars)
@@ -8,7 +8,11 @@ RSpec::Matchers.define :include_response do |expected_response|
8
8
 
9
9
  while !found_response && IO.select([read_io], nil, nil, read_timeout) do
10
10
  response += read_io.read_nonblock(read_bytes)
11
- found_response = response.include?(expected_response)
11
+ found_response = if expected_response.is_a? Regexp
12
+ response.match(expected_response)
13
+ else
14
+ response.include?(expected_response)
15
+ end
12
16
  end
13
17
 
14
18
  found_response
@@ -5,6 +5,7 @@
5
5
  #include "Reset.h"
6
6
  #include "Node.h"
7
7
  #include "Evaluate.h"
8
+ #include "EvaluateAsync.h"
8
9
  #include "Execute.h"
9
10
  #include "FrameFocus.h"
10
11
  #include "Header.h"
@@ -46,6 +47,7 @@
46
47
  #include "WindowMaximize.h"
47
48
  #include "GoBack.h"
48
49
  #include "GoForward.h"
50
+ #include "Refresh.h"
49
51
  #include "AcceptAlert.h"
50
52
  #include "FindModal.h"
51
53
  #include "SetUnknownUrlMode.h"
@@ -24,7 +24,7 @@ Connection::Connection(QTcpSocket *socket, WebPageManager *manager, QObject *par
24
24
  }
25
25
 
26
26
  void Connection::commandReady(Command *command) {
27
- m_manager->logger() << "Received" << command->toString();
27
+ m_manager->log() << "Received" << command->toString();
28
28
  startCommand(command);
29
29
  }
30
30
 
@@ -65,7 +65,7 @@ void Connection::writeResponse(Response *response) {
65
65
  else
66
66
  m_socket->write("failure\n");
67
67
 
68
- m_manager->logger() << "Wrote response" << response->isSuccess() << response->message();
68
+ m_manager->log() << "Wrote response" << response->isSuccess() << response->message();
69
69
 
70
70
  QByteArray messageUtf8 = response->message();
71
71
  QString messageLength = QString::number(messageUtf8.size()) + "\n";
@@ -18,8 +18,8 @@ void Evaluate::start() {
18
18
  QString eval_script = QString("(function(){"
19
19
  " for(var i=0; i<arguments.length; i++) {"
20
20
  " arguments[i] = JSON.parse(arguments[i]);"
21
- " var elem_id;"
22
- " if (elem_id = arguments[i]['element-581e-422e-8be1-884c4e116226']) {"
21
+ " var elem_id = arguments[i]['element-581e-422e-8be1-884c4e116226'];"
22
+ " if (elem_id) {"
23
23
  " arguments[i] = Capybara.getNode(elem_id);"
24
24
  " };"
25
25
  " };"
@@ -0,0 +1,54 @@
1
+ #include "EvaluateAsync.h"
2
+ #include "WebPage.h"
3
+ #include "WebPageManager.h"
4
+ #include "JsonSerializer.h"
5
+ #include <iostream>
6
+
7
+ EvaluateAsync::EvaluateAsync(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
8
+ }
9
+
10
+ void EvaluateAsync::start() {
11
+ QString script = arguments()[0];
12
+ QString jsonArgs;
13
+ if (arguments().length()>1){
14
+ jsonArgs = arguments()[1];
15
+ } else {
16
+ jsonArgs ="[]";
17
+ }
18
+ QString eval_script = QString("(function(){"
19
+ " for(var i=0; i<arguments.length-1; i++) {"
20
+ " arguments[i] = JSON.parse(arguments[i]);"
21
+ " var elem_id = arguments[i]['element-581e-422e-8be1-884c4e116226'];"
22
+ " if (elem_id) {"
23
+ " arguments[i] = Capybara.getNode(elem_id);"
24
+ " };"
25
+ " };"
26
+ " eval(\"%1\");"
27
+ " return;"
28
+ " }).apply(null, %2.concat(function(res){ "
29
+ " res = Capybara.wrapResult(res);"
30
+ // This isn't necessary in Qt 5.5 but is in older (at least 5.2 on Travis)
31
+ " if (res instanceof Array) {"
32
+ " CapybaraAsync['asyncResult(QVariantList)'](res);"
33
+ " } else { "
34
+ " CapybaraAsync['asyncResult(QVariant)'](res);"
35
+ " }"
36
+ " }))").arg(script.replace("\"","\\\"").remove("\n"), jsonArgs);
37
+ QObject invocation_stub;
38
+ invocation_stub.setProperty("allowUnattached", false);
39
+ page()->currentFrame()->addToJavaScriptWindowObject("CapybaraInvocation", &invocation_stub);
40
+ page()->currentFrame()->addToJavaScriptWindowObject("CapybaraAsync", this);
41
+ page()->currentFrame()->evaluateJavaScript(eval_script);
42
+ }
43
+
44
+ void EvaluateAsync::asyncResult(QVariantList result) {
45
+ JsonSerializer serializer;
46
+ finish(true, serializer.serialize(result));
47
+ }
48
+
49
+ void EvaluateAsync::asyncResult(QVariant result) {
50
+ JsonSerializer serializer;
51
+ finish(true, serializer.serialize(result));
52
+ }
53
+
54
+
@@ -0,0 +1,14 @@
1
+ #include "SocketCommand.h"
2
+
3
+ #include <QVariantList>
4
+
5
+ class EvaluateAsync : public SocketCommand {
6
+ Q_OBJECT
7
+
8
+ public:
9
+ EvaluateAsync(WebPageManager *, QStringList &arguments, QObject *parent = 0);
10
+ virtual void start();
11
+ Q_INVOKABLE void asyncResult(QVariant result);
12
+ Q_INVOKABLE void asyncResult(QVariantList result);
13
+ };
14
+
@@ -5,6 +5,8 @@
5
5
  #include <QEvent>
6
6
  #include <QContextMenuEvent>
7
7
 
8
+ QMap<QString, Qt::KeyboardModifiers> JavascriptInvocation::m_modifiersMap(JavascriptInvocation::makeModifiersMap());
9
+
8
10
  JavascriptInvocation::JavascriptInvocation(const QString &functionName, bool allowUnattached, const QStringList &arguments, WebPage *page, QObject *parent) : QObject(parent) {
9
11
  m_functionName = functionName;
10
12
  m_allowUnattached = allowUnattached;
@@ -33,6 +35,7 @@ void JavascriptInvocation::setError(QVariant error) {
33
35
  }
34
36
 
35
37
  InvocationResult JavascriptInvocation::invoke(QWebFrame *frame) {
38
+
36
39
  frame->addToJavaScriptWindowObject("CapybaraInvocation", this);
37
40
  QVariant result = frame->evaluateJavaScript("Capybara.invoke()");
38
41
  if (getError().isValid())
@@ -41,36 +44,43 @@ InvocationResult JavascriptInvocation::invoke(QWebFrame *frame) {
41
44
  if (functionName() == "leftClick") {
42
45
  // Don't trigger the left click from JS incase the frame closes
43
46
  QVariantMap qm = result.toMap();
44
- leftClick(qm["absoluteX"].toInt(), qm["absoluteY"].toInt());
47
+ leftClick(qm["absoluteX"].toInt(), qm["absoluteY"].toInt(), qm["keys"].value<QVariantList>());
45
48
  }
46
49
  return InvocationResult(result);
47
50
  }
48
51
  }
49
52
 
50
- void JavascriptInvocation::leftClick(int x, int y) {
53
+ Qt::KeyboardModifiers JavascriptInvocation::modifiers(const QVariantList& keys){
54
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
55
+ for (int i = 0; i < keys.length(); i++) {
56
+ modifiers |= m_modifiersMap.value(keys[i].toString(), Qt::NoModifier);
57
+ }
58
+ return modifiers;
59
+ }
60
+
61
+ void JavascriptInvocation::leftClick(int x, int y, QVariantList keys) {
51
62
  QPoint mousePos(x, y);
52
63
 
53
- m_page->mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::LeftButton);
54
- m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton);
64
+ m_page->mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::LeftButton, modifiers(keys));
65
+ m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton, modifiers(keys));
55
66
  }
56
67
 
57
- void JavascriptInvocation::rightClick(int x, int y) {
68
+ void JavascriptInvocation::rightClick(int x, int y, QVariantList keys) {
58
69
  QPoint mousePos(x, y);
59
-
60
- m_page->mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::RightButton);
70
+ m_page->mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::RightButton, modifiers(keys));
61
71
 
62
72
  // swallowContextMenuEvent tries to fire contextmenu event in html page
63
- QContextMenuEvent *event = new QContextMenuEvent(QContextMenuEvent::Mouse, mousePos);
73
+ QContextMenuEvent *event = new QContextMenuEvent(QContextMenuEvent::Mouse, mousePos, QCursor::pos(), modifiers(keys));
64
74
  m_page->swallowContextMenuEvent(event);
65
75
 
66
- m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::RightButton);
76
+ m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::RightButton, modifiers(keys));
67
77
  }
68
78
 
69
- void JavascriptInvocation::doubleClick(int x, int y) {
79
+ void JavascriptInvocation::doubleClick(int x, int y, QVariantList keys) {
70
80
  QPoint mousePos(x, y);
71
81
 
72
- m_page->mouseEvent(QEvent::MouseButtonDblClick, mousePos, Qt::LeftButton);
73
- m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton);
82
+ m_page->mouseEvent(QEvent::MouseButtonDblClick, mousePos, Qt::LeftButton, modifiers(keys));
83
+ m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton, modifiers(keys));
74
84
  }
75
85
 
76
86
  bool JavascriptInvocation::clickTest(QWebElement element, int absoluteX, int absoluteY) {
@@ -86,6 +96,8 @@ QVariantMap JavascriptInvocation::clickPosition(QWebElement element, int left, i
86
96
  QVariantMap m;
87
97
  m["relativeX"] = mousePos.x();
88
98
  m["relativeY"] = mousePos.y();
99
+ m["relativeTop"] = boundedBox.top();
100
+ m["relativeLeft"] = boundedBox.left();
89
101
 
90
102
  QWebFrame *parent = element.webFrame();
91
103
  while (parent) {
@@ -95,7 +107,8 @@ QVariantMap JavascriptInvocation::clickPosition(QWebElement element, int left, i
95
107
 
96
108
  boundedBox = elementBox.intersected(viewport);
97
109
  mousePos = boundedBox.center();
98
-
110
+ m["absoluteTop"] = boundedBox.top();
111
+ m["absoluteLeft"] = boundedBox.left();
99
112
  m["absoluteX"] = mousePos.x();
100
113
  m["absoluteY"] = mousePos.y();
101
114
 
@@ -217,3 +230,14 @@ const QString JavascriptInvocation::render(void) {
217
230
  m_page->render(path, QSize(1024, 768));
218
231
  return path;
219
232
  }
233
+
234
+ QMap<QString, Qt::KeyboardModifiers> JavascriptInvocation::makeModifiersMap(){
235
+ QMap<QString, Qt::KeyboardModifiers> map;
236
+ map["alt"] = Qt::AltModifier;
237
+ map["control"] = Qt::ControlModifier;
238
+ map["meta"] = Qt::MetaModifier;
239
+ map["shift"] = Qt::ShiftModifier;
240
+ return map;
241
+ }
242
+
243
+
@@ -20,9 +20,9 @@ class JavascriptInvocation : public QObject {
20
20
  QString &functionName();
21
21
  bool allowUnattached();
22
22
  QStringList &arguments();
23
- Q_INVOKABLE void leftClick(int x, int y);
24
- Q_INVOKABLE void rightClick(int x, int y);
25
- Q_INVOKABLE void doubleClick(int x, int y);
23
+ Q_INVOKABLE void leftClick(int x, int y, QVariantList keys);
24
+ Q_INVOKABLE void rightClick(int x, int y, QVariantList keys);
25
+ Q_INVOKABLE void doubleClick(int x, int y, QVariantList keys);
26
26
  Q_INVOKABLE bool clickTest(QWebElement element, int absoluteX, int absoluteY);
27
27
  Q_INVOKABLE QVariantMap clickPosition(QWebElement element, int left, int top, int width, int height);
28
28
  Q_INVOKABLE void hover(int absoluteX, int absoluteY);
@@ -46,5 +46,8 @@ class JavascriptInvocation : public QObject {
46
46
  int keyCodeForName(const QString &);
47
47
  Qt::Key key_enum;
48
48
  Qt::KeyboardModifiers m_currentModifiers;
49
+ Qt::KeyboardModifiers modifiers(const QVariantList& keys);
50
+ static QMap<QString, Qt::KeyboardModifiers> makeModifiersMap();
51
+ static QMap<QString, Qt::KeyboardModifiers> m_modifiersMap;
49
52
  };
50
53
 
@@ -1,6 +1,7 @@
1
1
  #include "Node.h"
2
2
  #include "WebPage.h"
3
3
  #include "WebPageManager.h"
4
+ #include "JsonSerializer.h"
4
5
  #include "InvocationResult.h"
5
6
 
6
7
  Node::Node(WebPageManager *manager, QStringList &arguments, QObject *parent) : JavascriptCommand(manager, arguments, parent) {
@@ -14,7 +15,14 @@ void Node::start() {
14
15
  if (functionName == "focus_frame") {
15
16
  page()->setCurrentFrameParent(page()->currentFrame()->parentFrame());
16
17
  }
17
- finish(&result);
18
+
19
+ if (result.hasError()) {
20
+ finish(&result);
21
+ } else {
22
+ JsonSerializer serializer;
23
+ InvocationResult jsonResult = InvocationResult(serializer.serialize(result.result()));
24
+ finish(&jsonResult);
25
+ }
18
26
  }
19
27
 
20
28
  QString Node::toString() const {
@@ -14,7 +14,7 @@ PageLoadingCommand::PageLoadingCommand(Command *command, WebPageManager *manager
14
14
  }
15
15
 
16
16
  void PageLoadingCommand::start() {
17
- m_manager->logger() << "Started" << m_command->toString();
17
+ m_manager->log() << "Started" << m_command->toString();
18
18
  connect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *)));
19
19
  connect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
20
20
  connect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
@@ -26,7 +26,7 @@ void PageLoadingCommand::pendingLoadFinished(bool success) {
26
26
  if (m_pageLoadingFromCommand) {
27
27
  m_pageLoadingFromCommand = false;
28
28
  if (m_pendingResponse) {
29
- m_manager->logger() << "Page load from command finished";
29
+ m_manager->log() << "Page load from command finished";
30
30
  if (m_pageSuccess) {
31
31
  emit finished(m_pendingResponse);
32
32
  } else {
@@ -38,13 +38,13 @@ void PageLoadingCommand::pendingLoadFinished(bool success) {
38
38
  }
39
39
 
40
40
  void PageLoadingCommand::pageLoadingFromCommand() {
41
- m_manager->logger() << m_command->toString() << "started page load";
41
+ m_manager->log() << m_command->toString() << "started page load";
42
42
  m_pageLoadingFromCommand = true;
43
43
  }
44
44
 
45
45
  void PageLoadingCommand::commandFinished(Response *response) {
46
46
  disconnect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
47
- m_manager->logger() << "Finished" << m_command->toString() << "with response" << response->toString();
47
+ m_manager->log() << "Finished" << m_command->toString() << "with response" << response->toString();
48
48
 
49
49
  if (m_pageLoadingFromCommand)
50
50
  m_pendingResponse = response;
@@ -0,0 +1,12 @@
1
+ #include "Refresh.h"
2
+ #include "SocketCommand.h"
3
+ #include "WebPage.h"
4
+ #include "WebPageManager.h"
5
+
6
+ Refresh::Refresh(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7
+ }
8
+
9
+ void Refresh::start() {
10
+ page()->triggerAction(QWebPage::Reload);
11
+ finish(true);
12
+ }
@@ -0,0 +1,10 @@
1
+ #include "SocketCommand.h"
2
+
3
+ class Refresh : public SocketCommand {
4
+ Q_OBJECT
5
+
6
+ public:
7
+ Refresh(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8
+ virtual void start();
9
+ };
10
+
@@ -19,7 +19,7 @@ TimeoutCommand::TimeoutCommand(Command *command, WebPageManager *manager, QObjec
19
19
  void TimeoutCommand::start() {
20
20
  QApplication::processEvents();
21
21
  if (m_manager->isLoading()) {
22
- m_manager->logger() << this->toString() << "waiting for load to finish";
22
+ m_manager->log() << this->toString() << "waiting for load to finish";
23
23
  connect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
24
24
  startTimeout();
25
25
  } else {
@@ -25,7 +25,10 @@ WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
25
25
 
26
26
  setForwardUnsupportedContent(true);
27
27
  loadJavascript();
28
- setUserStylesheet();
28
+
29
+ #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
30
+ setUserStylesheet();
31
+ #endif
29
32
 
30
33
  this->setCustomNetworkAccessManager();
31
34
 
@@ -174,7 +177,7 @@ void WebPage::javaScriptConsoleMessage(const QString &message, int lineNumber, c
174
177
  m["line_number"] = lineNumber;
175
178
  }
176
179
  m_consoleMessages.append(m);
177
- m_manager->logger() << qPrintable(fullMessage);
180
+ m_manager->log() << qPrintable(fullMessage);
178
181
  }
179
182
 
180
183
  void WebPage::javaScriptAlert(QWebFrame *frame, const QString &message) {
@@ -191,7 +194,7 @@ void WebPage::javaScriptAlert(QWebFrame *frame, const QString &message) {
191
194
  addModalMessage(expectedType, message, expectedMessage);
192
195
  }
193
196
 
194
- m_manager->logger() << "ALERT:" << qPrintable(message);
197
+ m_manager->log() << "ALERT:" << qPrintable(message);
195
198
  }
196
199
 
197
200
  bool WebPage::javaScriptConfirm(QWebFrame *frame, const QString &message) {
@@ -272,9 +275,9 @@ QString WebPage::failureString() {
272
275
  return message + m_errorPageMessage;
273
276
  }
274
277
 
275
- void WebPage::mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button) {
278
+ void WebPage::mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button, Qt::KeyboardModifiers modifiers) {
276
279
  m_mousePosition = position;
277
- QMouseEvent event(type, position, button, button, Qt::NoModifier);
280
+ QMouseEvent event(type, position, button, button, modifiers);
278
281
  QApplication::sendEvent(this, &event);
279
282
  }
280
283
 
@@ -488,5 +491,3 @@ QWebFrame* WebPage::currentFrameParent() {
488
491
  void WebPage::setCurrentFrameParent(QWebFrame* frame) {
489
492
  m_currentFrameParent = frame;
490
493
  }
491
-
492
-