capybara-webkit 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }