capybara-webkit 1.1.1 → 1.2.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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +19 -13
  4. data/Appraisals +8 -4
  5. data/Gemfile.lock +29 -14
  6. data/NEWS.md +4 -0
  7. data/README.md +1 -1
  8. data/Rakefile +1 -1
  9. data/capybara-webkit.gemspec +2 -1
  10. data/gemfiles/2.0.gemfile.lock +10 -6
  11. data/gemfiles/2.1.gemfile.lock +22 -9
  12. data/gemfiles/2.2.gemfile +7 -0
  13. data/gemfiles/2.2.gemfile.lock +77 -0
  14. data/gemfiles/2.3.gemfile +7 -0
  15. data/gemfiles/2.3.gemfile.lock +77 -0
  16. data/lib/capybara/webkit/browser.rb +28 -4
  17. data/lib/capybara/webkit/connection.rb +9 -24
  18. data/lib/capybara/webkit/driver.rb +40 -4
  19. data/lib/capybara/webkit/errors.rb +3 -0
  20. data/lib/capybara/webkit/version.rb +1 -1
  21. data/spec/browser_spec.rb +1 -1
  22. data/spec/connection_spec.rb +27 -5
  23. data/spec/driver_rendering_spec.rb +10 -0
  24. data/spec/driver_resize_window_spec.rb +22 -0
  25. data/spec/driver_spec.rb +117 -9
  26. data/spec/selenium_compatibility_spec.rb +3 -0
  27. data/spec/spec_helper.rb +22 -3
  28. data/spec/support/matchers/include_response.rb +24 -0
  29. data/src/CommandFactory.cpp +7 -1
  30. data/src/GoBack.cpp +12 -0
  31. data/src/GoBack.h +10 -0
  32. data/src/GoForward.cpp +12 -0
  33. data/src/GoForward.h +10 -0
  34. data/src/Render.cpp +10 -1
  35. data/src/Reset.cpp +0 -2
  36. data/src/SocketCommand.cpp +6 -0
  37. data/src/SocketCommand.h +1 -0
  38. data/src/StdinNotifier.cpp +16 -0
  39. data/src/StdinNotifier.h +20 -0
  40. data/src/WebPage.cpp +20 -5
  41. data/src/WebPage.h +4 -1
  42. data/src/WebPageManager.cpp +18 -7
  43. data/src/WebPageManager.h +2 -1
  44. data/src/WindowClose.cpp +10 -0
  45. data/src/WindowClose.h +12 -0
  46. data/src/WindowCommand.cpp +27 -0
  47. data/src/WindowCommand.h +21 -0
  48. data/src/WindowFocus.cpp +2 -25
  49. data/src/WindowFocus.h +4 -7
  50. data/src/WindowMaximize.cpp +14 -0
  51. data/src/WindowMaximize.h +12 -0
  52. data/src/WindowOpen.cpp +12 -0
  53. data/src/WindowOpen.h +10 -0
  54. data/src/WindowResize.cpp +16 -0
  55. data/src/WindowResize.h +12 -0
  56. data/src/WindowSize.cpp +17 -0
  57. data/src/WindowSize.h +12 -0
  58. data/src/capybara.js +19 -3
  59. data/src/find_command.h +7 -1
  60. data/src/main.cpp +4 -0
  61. data/src/webkit_server.pro +20 -4
  62. data/templates/Command.cpp +2 -0
  63. data/templates/Command.h +1 -3
  64. metadata +82 -54
  65. data/src/ResizeWindow.cpp +0 -17
  66. data/src/ResizeWindow.h +0 -10
@@ -10,6 +10,8 @@ describe Capybara::Webkit, 'compatibility with selenium' do
10
10
  <label for="one">One</label><input type="text" name="one" id="one" />
11
11
  <label for="two">Two</label><input type="text" name="two" id="two" />
12
12
  <label for="three">Three</label><input type="text" name="three" id="three" readonly="readonly" />
13
+ <label for="textarea">Textarea</label>
14
+ <textarea name="textarea" id="textarea"></textarea>
13
15
  <input type="submit" value="Submit" id="submit" />
14
16
  </form>
15
17
  <script type="text/javascript">
@@ -35,6 +37,7 @@ describe Capybara::Webkit, 'compatibility with selenium' do
35
37
  fill_in "One", :with => "a new value"
36
38
  fill_in "Two", :with => "other value"
37
39
  fill_in "Three", :with => "readonly value"
40
+ fill_in "Textarea", :with => "last value"
38
41
  click_button "Submit"
39
42
  end
40
43
  end
data/spec/spec_helper.rb CHANGED
@@ -10,8 +10,8 @@ $LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
10
10
  Dir[File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')].each { |file| require(file) }
11
11
 
12
12
  require 'capybara/webkit'
13
- connection = Capybara::Webkit::Connection.new(:socket_class => TCPSocket)
14
- $webkit_browser = Capybara::Webkit::Browser.new(connection)
13
+ $webkit_connection = Capybara::Webkit::Connection.new(:socket_class => TCPSocket)
14
+ $webkit_browser = Capybara::Webkit::Browser.new($webkit_connection)
15
15
 
16
16
  if ENV['DEBUG']
17
17
  $webkit_browser.enable_logging
@@ -24,8 +24,27 @@ Capybara.register_driver :reusable_webkit do |app|
24
24
  end
25
25
 
26
26
  RSpec.configure do |c|
27
- c.filter_run_excluding :skip_on_windows => !(RbConfig::CONFIG['host_os'] =~ /mingw32/).nil?
28
27
  Capybara::SpecHelper.configure(c)
28
+
29
+ c.filter_run_excluding :skip_on_windows => !(RbConfig::CONFIG['host_os'] =~ /mingw32/).nil?
30
+ c.filter_run_excluding :skip_on_jruby => !defined?(::JRUBY_VERSION).nil?
31
+
32
+ # We can't support outerWidth and outerHeight without a visible window.
33
+ # We focus the next window instead of failing when closing windows.
34
+ c.filter_run_excluding :full_description =>
35
+ /Capybara::Session webkit Capybara::Window #(size|resize_to|maximize|close.*no_such_window_error)/
36
+
37
+ # Capybara's integration tests expect "capybara/" in the default path
38
+ c.around :requires => :screenshot do |example|
39
+ old_path = Capybara.save_and_open_page_path
40
+ Capybara.save_and_open_page_path = File.join(PROJECT_ROOT, 'tmp', 'capybara')
41
+
42
+ begin
43
+ example.run
44
+ ensure
45
+ Capybara.save_and_open_page_path = old_path
46
+ end
47
+ end
29
48
  end
30
49
 
31
50
  def with_env_vars(vars)
@@ -0,0 +1,24 @@
1
+ RSpec::Matchers.define :include_response do |expected_response|
2
+ read_timeout = 2
3
+ read_bytes = 4096
4
+ response = ""
5
+
6
+ match do |read_io|
7
+ found_response = false
8
+
9
+ while !found_response && IO.select([read_io], nil, nil, read_timeout) do
10
+ response += read_io.read_nonblock(read_bytes)
11
+ found_response = response.include?(expected_response)
12
+ end
13
+
14
+ found_response
15
+ end
16
+
17
+ failure_message_for_should do |actual|
18
+ "expected #{response} to include #{expected_response}"
19
+ end
20
+
21
+ failure_message_for_should_not do |actual|
22
+ "expected #{response} to not include #{expected_response}"
23
+ end
24
+ end
@@ -20,7 +20,7 @@
20
20
  #include "CurrentUrl.h"
21
21
  #include "SetTimeout.h"
22
22
  #include "GetTimeout.h"
23
- #include "ResizeWindow.h"
23
+ #include "WindowResize.h"
24
24
  #include "IgnoreSslErrors.h"
25
25
  #include "SetSkipImageLoading.h"
26
26
  #include "WindowFocus.h"
@@ -40,6 +40,12 @@
40
40
  #include "Version.h"
41
41
  #include "Title.h"
42
42
  #include "FindCss.h"
43
+ #include "WindowClose.h"
44
+ #include "WindowOpen.h"
45
+ #include "WindowSize.h"
46
+ #include "WindowMaximize.h"
47
+ #include "GoBack.h"
48
+ #include "GoForward.h"
43
49
 
44
50
  CommandFactory::CommandFactory(WebPageManager *manager, QObject *parent) : QObject(parent) {
45
51
  m_manager = manager;
data/src/GoBack.cpp ADDED
@@ -0,0 +1,12 @@
1
+ #include "GoBack.h"
2
+ #include "SocketCommand.h"
3
+ #include "WebPage.h"
4
+ #include "WebPageManager.h"
5
+
6
+ GoBack::GoBack(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7
+ }
8
+
9
+ void GoBack::start() {
10
+ page()->triggerAction(QWebPage::Back);
11
+ finish(true);
12
+ }
data/src/GoBack.h ADDED
@@ -0,0 +1,10 @@
1
+ #include "SocketCommand.h"
2
+
3
+ class GoBack : public SocketCommand {
4
+ Q_OBJECT
5
+
6
+ public:
7
+ GoBack(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8
+ virtual void start();
9
+ };
10
+
data/src/GoForward.cpp ADDED
@@ -0,0 +1,12 @@
1
+ #include "GoForward.h"
2
+ #include "SocketCommand.h"
3
+ #include "WebPage.h"
4
+ #include "WebPageManager.h"
5
+
6
+ GoForward::GoForward(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7
+ }
8
+
9
+ void GoForward::start() {
10
+ page()->triggerAction(QWebPage::Forward);
11
+ finish(true);
12
+ }
data/src/GoForward.h ADDED
@@ -0,0 +1,10 @@
1
+ #include "SocketCommand.h"
2
+
3
+ class GoForward : public SocketCommand {
4
+ Q_OBJECT
5
+
6
+ public:
7
+ GoForward(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8
+ virtual void start();
9
+ };
10
+
data/src/Render.cpp CHANGED
@@ -1,6 +1,7 @@
1
1
  #include "Render.h"
2
2
  #include "WebPage.h"
3
3
  #include "WebPageManager.h"
4
+ #include "ErrorMessage.h"
4
5
 
5
6
  Render::Render(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
6
7
  }
@@ -14,5 +15,13 @@ void Render::start() {
14
15
 
15
16
  bool result = page()->render( imagePath, size );
16
17
 
17
- finish(result);
18
+ if (result) {
19
+ finish(true);
20
+ } else {
21
+ const QString failure = QString("Unable to save %1x%2 image to %3").
22
+ arg(width).
23
+ arg(height).
24
+ arg(imagePath);
25
+ finish(false, new ErrorMessage(failure));
26
+ }
18
27
  }
data/src/Reset.cpp CHANGED
@@ -6,8 +6,6 @@ Reset::Reset(WebPageManager *manager, QStringList &arguments, QObject *parent) :
6
6
  }
7
7
 
8
8
  void Reset::start() {
9
- page()->triggerAction(QWebPage::Stop);
10
-
11
9
  manager()->reset();
12
10
 
13
11
  finish(true);
@@ -19,3 +19,9 @@ WebPageManager *SocketCommand::manager() const {
19
19
  return m_manager;
20
20
  }
21
21
 
22
+ QString SocketCommand::toString() const {
23
+ QString result;
24
+ QTextStream(&result) << metaObject()->className() << QString("(") << m_arguments.join(", ") << QString(")");
25
+ return result;
26
+ }
27
+
data/src/SocketCommand.h CHANGED
@@ -14,6 +14,7 @@ class SocketCommand : public Command {
14
14
 
15
15
  public:
16
16
  SocketCommand(WebPageManager *, QStringList &arguments, QObject *parent = 0);
17
+ virtual QString toString() const;
17
18
 
18
19
  protected:
19
20
  WebPage *page() const;
@@ -0,0 +1,16 @@
1
+ #include "StdinNotifier.h"
2
+
3
+ #include <QTcpServer>
4
+
5
+ StdinNotifier::StdinNotifier(QObject *parent) : QObject(parent) {
6
+ m_notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this);
7
+ connect(m_notifier, SIGNAL(activated(int)), this, SLOT(notifierActivated()));
8
+ }
9
+
10
+ void StdinNotifier::notifierActivated() {
11
+ std::string line;
12
+ std::getline(std::cin, line);
13
+ if (std::cin.eof()) {
14
+ emit eof();
15
+ }
16
+ }
@@ -0,0 +1,20 @@
1
+ #include <QObject>
2
+
3
+ class QSocketNotifier;
4
+
5
+ class StdinNotifier : public QObject {
6
+ Q_OBJECT
7
+
8
+ public:
9
+ StdinNotifier(QObject *parent = 0);
10
+
11
+ public slots:
12
+ void notifierActivated();
13
+
14
+ signals:
15
+ void eof();
16
+
17
+ private:
18
+ QSocketNotifier *m_notifier;
19
+ };
20
+
data/src/WebPage.cpp CHANGED
@@ -11,6 +11,8 @@
11
11
  #include <QWebSettings>
12
12
  #include <QUuid>
13
13
  #include <QApplication>
14
+ #include <QWebView>
15
+ #include <QMainWindow>
14
16
 
15
17
  WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
16
18
  m_loading = false;
@@ -33,14 +35,23 @@ WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
33
35
  this, SLOT(frameCreated(QWebFrame *)));
34
36
  connect(this, SIGNAL(unsupportedContent(QNetworkReply*)),
35
37
  this, SLOT(handleUnsupportedContent(QNetworkReply*)));
36
- resetWindowSize();
38
+ connect(this, SIGNAL(windowCloseRequested()), this, SLOT(remove()));
37
39
 
38
40
  settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
41
+ settings()->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
42
+ settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
43
+
44
+ createWindow();
45
+ }
46
+
47
+ void WebPage::createWindow() {
48
+ QSize size(1680, 1050);
49
+ setViewportSize(size);
39
50
  }
40
51
 
41
- void WebPage::resetWindowSize() {
42
- this->setViewportSize(QSize(1680, 1050));
43
- this->settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
52
+ void WebPage::resize(int width, int height) {
53
+ QSize size(width, height);
54
+ setViewportSize(size);
44
55
  }
45
56
 
46
57
  void WebPage::resetLocalStorage() {
@@ -334,7 +345,7 @@ bool WebPage::supportsExtension(Extension extension) const {
334
345
 
335
346
  QWebPage *WebPage::createWindow(WebWindowType type) {
336
347
  Q_UNUSED(type);
337
- return m_manager->createPage(this);
348
+ return m_manager->createPage();
338
349
  }
339
350
 
340
351
  QString WebPage::uuid() {
@@ -361,6 +372,10 @@ void WebPage::setFocus() {
361
372
  m_manager->setCurrentPage(this);
362
373
  }
363
374
 
375
+ void WebPage::remove() {
376
+ m_manager->removePage(this);
377
+ }
378
+
364
379
  void WebPage::setConfirmAction(QString action) {
365
380
  m_confirm = (action == "Yes");
366
381
  }
data/src/WebPage.h CHANGED
@@ -10,6 +10,7 @@
10
10
  class WebPageManager;
11
11
  class InvocationResult;
12
12
  class NetworkReplyProxy;
13
+ class QWebView;
13
14
 
14
15
  class WebPage : public QWebPage {
15
16
  Q_OBJECT
@@ -33,7 +34,7 @@ class WebPage : public QWebPage {
33
34
  QVariantList alertMessages();
34
35
  QVariantList confirmMessages();
35
36
  QVariantList promptMessages();
36
- void resetWindowSize();
37
+ void createWindow();
37
38
  void resetLocalStorage();
38
39
  QWebPage *createWindow(WebWindowType type);
39
40
  QString uuid();
@@ -46,6 +47,7 @@ class WebPage : public QWebPage {
46
47
  QString contentType();
47
48
  void mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button);
48
49
  bool clickTest(QWebElement element, int absoluteX, int absoluteY);
50
+ void resize(int, int);
49
51
 
50
52
  public slots:
51
53
  bool shouldInterruptJavaScript();
@@ -57,6 +59,7 @@ class WebPage : public QWebPage {
57
59
  void handleSslErrorsForReply(QNetworkReply *reply, const QList<QSslError> &);
58
60
  void handleUnsupportedContent(QNetworkReply *reply);
59
61
  void replyFinished(QUrl &, QNetworkReply *);
62
+ void remove();
60
63
 
61
64
  signals:
62
65
  void pageFinished(bool);
@@ -12,7 +12,7 @@ WebPageManager::WebPageManager(QObject *parent) : QObject(parent) {
12
12
  m_timeout = -1;
13
13
  m_networkAccessManager = new NetworkAccessManager(this);
14
14
  m_networkAccessManager->setCookieJar(m_cookieJar);
15
- createPage(this)->setFocus();
15
+ createPage()->setFocus();
16
16
  }
17
17
 
18
18
  NetworkAccessManager *WebPageManager::networkAccessManager() {
@@ -35,8 +35,8 @@ WebPage *WebPageManager::currentPage() const {
35
35
  return m_currentPage;
36
36
  }
37
37
 
38
- WebPage *WebPageManager::createPage(QObject *parent) {
39
- WebPage *page = new WebPage(this, parent);
38
+ WebPage *WebPageManager::createPage() {
39
+ WebPage *page = new WebPage(this);
40
40
  connect(page, SIGNAL(loadStarted()),
41
41
  this, SLOT(emitLoadStarted()));
42
42
  connect(page, SIGNAL(pageFinished(bool)),
@@ -47,6 +47,15 @@ WebPage *WebPageManager::createPage(QObject *parent) {
47
47
  return page;
48
48
  }
49
49
 
50
+ void WebPageManager::removePage(WebPage *page) {
51
+ m_pages.removeOne(page);
52
+ page->deleteLater();
53
+ if (m_pages.isEmpty())
54
+ createPage()->setFocus();
55
+ else if (page == m_currentPage)
56
+ m_pages.first()->setFocus();
57
+ }
58
+
50
59
  void WebPageManager::emitLoadStarted() {
51
60
  if (m_started.empty()) {
52
61
  logger() << "Load started";
@@ -110,10 +119,12 @@ void WebPageManager::reset() {
110
119
  m_timeout = -1;
111
120
  m_cookieJar->clearCookies();
112
121
  m_networkAccessManager->reset();
113
- m_pages.first()->resetLocalStorage();
114
- m_pages.first()->deleteLater();
115
- m_pages.clear();
116
- createPage(this)->setFocus();
122
+ m_currentPage->resetLocalStorage();
123
+ while (!m_pages.isEmpty()) {
124
+ WebPage *page = m_pages.takeFirst();
125
+ page->deleteLater();
126
+ }
127
+ createPage()->setFocus();
117
128
  }
118
129
 
119
130
  NetworkCookieJar *WebPageManager::cookieJar() {
data/src/WebPageManager.h CHANGED
@@ -20,7 +20,8 @@ class WebPageManager : public QObject {
20
20
  QList<WebPage *> pages() const;
21
21
  void setCurrentPage(WebPage *);
22
22
  WebPage *currentPage() const;
23
- WebPage *createPage(QObject *parent);
23
+ WebPage *createPage();
24
+ void removePage(WebPage *);
24
25
  void setIgnoreSslErrors(bool);
25
26
  bool ignoreSslErrors();
26
27
  void setTimeout(int);
@@ -0,0 +1,10 @@
1
+ #include "WindowClose.h"
2
+ #include "WebPage.h"
3
+
4
+ WindowClose::WindowClose(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
5
+ }
6
+
7
+ void WindowClose::windowFound(WebPage *page) {
8
+ page->remove();
9
+ finish(true);
10
+ }
data/src/WindowClose.h ADDED
@@ -0,0 +1,12 @@
1
+ #include "WindowCommand.h"
2
+
3
+ class WindowClose : public WindowCommand {
4
+ Q_OBJECT
5
+
6
+ public:
7
+ WindowClose(WebPageManager *, QStringList &arguments, QObject *parent = 0);
8
+
9
+ protected:
10
+ virtual void windowFound(WebPage *);
11
+ };
12
+
@@ -0,0 +1,27 @@
1
+ #include "WindowCommand.h"
2
+ #include "WebPage.h"
3
+ #include "WebPageManager.h"
4
+ #include "ErrorMessage.h"
5
+
6
+ WindowCommand::WindowCommand(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
7
+ }
8
+
9
+ void WindowCommand::start() {
10
+ findWindow(arguments()[0]);
11
+ }
12
+
13
+ void WindowCommand::findWindow(QString selector) {
14
+ foreach(WebPage *page, manager()->pages()) {
15
+ if (page->matchesWindowSelector(selector)) {
16
+ windowFound(page);
17
+ return;
18
+ }
19
+ }
20
+
21
+ windowNotFound();
22
+ }
23
+
24
+ void WindowCommand::windowNotFound() {
25
+ finish(false,
26
+ new ErrorMessage("NoSuchWindowError", "Unable to locate window."));
27
+ }