capybara-webkit 0.14.2 → 1.0.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 (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
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Capybara::Webkit::JsonError do
4
+ let(:error) { described_class.new '{"class": "ClickFailed", "message": "Error clicking this element"}' }
5
+
6
+ subject { error.exception }
7
+
8
+ it { should be_an_instance_of Capybara::Webkit::ClickFailed }
9
+
10
+ its(:message) { should == 'Error clicking this element' }
11
+ end
@@ -60,6 +60,7 @@ describe Capybara::Session do
60
60
  <strong>Hello</strong>
61
61
  <span>UTF8文字列</span>
62
62
  <input type="button" value="ボタン" />
63
+ <a href="about:blank">Link</a>
63
64
  </body></html>
64
65
  HTML
65
66
  [200,
@@ -84,6 +85,13 @@ describe Capybara::Session do
84
85
  it "can click utf8 string" do
85
86
  subject.click_button('ボタン')
86
87
  end
88
+
89
+ it "raises an ElementNotFound error when the selector scope is no longer valid" do
90
+ subject.within('//body') do
91
+ subject.click_link 'Link'
92
+ lambda { subject.find('//strong') }.should raise_error(Capybara::ElementNotFound)
93
+ end
94
+ end
87
95
  end
88
96
 
89
97
  context "response headers with status code" do
@@ -228,4 +236,240 @@ describe Capybara::Session do
228
236
  subject.should have_content('admin')
229
237
  end
230
238
  end
239
+
240
+ context "iframe app" do
241
+ before(:all) do
242
+ @app = Class.new(ExampleApp) do
243
+ get '/' do
244
+ <<-HTML
245
+ <!DOCTYPE html>
246
+ <html>
247
+ <body>
248
+ <h1>Main Frame</h1>
249
+ <iframe src="/a" name="a_frame" width="500" height="500"></iframe>
250
+ </body>
251
+ </html>
252
+ HTML
253
+ end
254
+
255
+ get '/a' do
256
+ <<-HTML
257
+ <!DOCTYPE html>
258
+ <html>
259
+ <body>
260
+ <h1>Page A</h1>
261
+ <iframe src="/b" name="b_frame" width="500" height="500"></iframe>
262
+ </body>
263
+ </html>
264
+ HTML
265
+ end
266
+
267
+ get '/b' do
268
+ <<-HTML
269
+ <!DOCTYPE html>
270
+ <html>
271
+ <body>
272
+ <h1>Page B</h1>
273
+ <form action="/c" method="post">
274
+ <input id="button" name="commit" type="submit" value="B Button">
275
+ </form>
276
+ </body>
277
+ </html>
278
+ HTML
279
+ end
280
+
281
+ post '/c' do
282
+ <<-HTML
283
+ <!DOCTYPE html>
284
+ <html>
285
+ <body>
286
+ <h1>Page C</h1>
287
+ </body>
288
+ </html>
289
+ HTML
290
+ end
291
+ end
292
+ end
293
+
294
+ it 'supports clicking an element offset from the viewport origin' do
295
+ subject.visit '/'
296
+
297
+ subject.within_frame 'a_frame' do
298
+ subject.within_frame 'b_frame' do
299
+ subject.click_button 'B Button'
300
+ subject.should have_content('Page C')
301
+ end
302
+ end
303
+ end
304
+
305
+ it 'raises an error if an element is obscured when clicked' do
306
+ subject.visit('/')
307
+
308
+ subject.execute_script(<<-JS)
309
+ var div = document.createElement('div');
310
+ div.style.position = 'absolute';
311
+ div.style.left = '0px';
312
+ div.style.top = '0px';
313
+ div.style.width = '100%';
314
+ div.style.height = '100%';
315
+ document.body.appendChild(div);
316
+ JS
317
+
318
+ subject.within_frame('a_frame') do
319
+ subject.within_frame('b_frame') do
320
+ lambda {
321
+ subject.click_button 'B Button'
322
+ }.should raise_error(Capybara::Webkit::ClickFailed)
323
+ end
324
+ end
325
+ end
326
+ end
327
+
328
+ context 'click tests' do
329
+ before(:all) do
330
+ @app = Class.new(ExampleApp) do
331
+ get '/' do
332
+ <<-HTML
333
+ <!DOCTYPE html>
334
+ <html>
335
+ <head>
336
+ <style>
337
+ body {
338
+ width: 800px;
339
+ margin: 0;
340
+ }
341
+ .target {
342
+ width: 200px;
343
+ height: 200px;
344
+ float: left;
345
+ margin: 100px;
346
+ }
347
+ #offscreen {
348
+ position: absolute;
349
+ left: -5000px;
350
+ }
351
+ </style>
352
+ <body>
353
+ <div id="one" class="target"></div>
354
+ <div id="two" class="target"></div>
355
+ <div id="offscreen"><a href="/" id="foo">Click Me</a></div>
356
+ <form>
357
+ <input type="checkbox" id="bar">
358
+ </form>
359
+ <div><a href="#"><i></i>Some link</a></div>
360
+ <script type="text/javascript">
361
+ var targets = document.getElementsByClassName('target');
362
+ for (var i = 0; i < targets.length; i++) {
363
+ var target = targets[i];
364
+ target.onclick = function(event) {
365
+ this.setAttribute('data-click-x', event.clientX);
366
+ this.setAttribute('data-click-y', event.clientY);
367
+ };
368
+ }
369
+ </script>
370
+ </body>
371
+ </html>
372
+ HTML
373
+ end
374
+ end
375
+ end
376
+
377
+ it 'clicks in the center of an element' do
378
+ subject.visit('/')
379
+ subject.find(:css, '#one').click
380
+ subject.find(:css, '#one')['data-click-x'].should == '199'
381
+ subject.find(:css, '#one')['data-click-y'].should == '199'
382
+ end
383
+
384
+ it 'clicks in the center of the viewable area of an element' do
385
+ subject.visit('/')
386
+ subject.driver.resize_window(200, 200)
387
+ subject.find(:css, '#one').click
388
+ subject.find(:css, '#one')['data-click-x'].should == '149'
389
+ subject.find(:css, '#one')['data-click-y'].should == '99'
390
+ end
391
+
392
+ it 'does not raise an error when an anchor contains empty nodes' do
393
+ subject.visit('/')
394
+ lambda { subject.click_link('Some link') }.should_not raise_error(Capybara::Webkit::ClickFailed)
395
+ end
396
+
397
+ it 'scrolls an element into view when clicked' do
398
+ subject.visit('/')
399
+ subject.driver.resize_window(200, 200)
400
+ subject.find(:css, '#two').click
401
+ subject.find(:css, '#two')['data-click-x'].should_not be_nil
402
+ subject.find(:css, '#two')['data-click-y'].should_not be_nil
403
+ end
404
+
405
+ it 'raises an error if an element is obscured when clicked' do
406
+ subject.visit('/')
407
+
408
+ subject.execute_script(<<-JS)
409
+ var two = document.getElementById('two');
410
+ two.style.position = 'absolute';
411
+ two.style.left = '0px';
412
+ two.style.top = '0px';
413
+ JS
414
+
415
+ lambda {
416
+ subject.find(:css, '#one').click
417
+ }.should raise_error(Capybara::Webkit::ClickFailed, /\[@id='one'\] at position/)
418
+ end
419
+
420
+ it 'raises an error if a checkbox is obscured when checked' do
421
+ subject.visit('/')
422
+
423
+ subject.execute_script(<<-JS)
424
+ var div = document.createElement('div');
425
+ div.style.position = 'absolute';
426
+ div.style.left = '0px';
427
+ div.style.top = '0px';
428
+ div.style.width = '100%';
429
+ div.style.height = '100%';
430
+ document.body.appendChild(div);
431
+ JS
432
+
433
+ lambda {
434
+ subject.check('bar')
435
+ }.should raise_error(Capybara::Webkit::ClickFailed)
436
+ end
437
+
438
+ it 'raises an error if an element is not visible when clicked' do
439
+ ignore_hidden_elements = Capybara.ignore_hidden_elements
440
+ Capybara.ignore_hidden_elements = false
441
+ begin
442
+ subject.visit('/')
443
+ subject.execute_script "document.getElementById('foo').style.display = 'none'"
444
+ lambda { subject.click_link "Click Me" }.should raise_error(Capybara::Webkit::ClickFailed, /\[@id='foo'\] at unknown/)
445
+ ensure
446
+ Capybara.ignore_hidden_elements = ignore_hidden_elements
447
+ end
448
+ end
449
+
450
+ it 'raises an error if an element is not in the viewport when clicked' do
451
+ subject.visit('/')
452
+ lambda { subject.click_link "Click Me" }.should raise_error(Capybara::Webkit::ClickFailed)
453
+ end
454
+
455
+ context "with wait time of 1 second" do
456
+ around do |example|
457
+ default_wait_time = Capybara.default_wait_time
458
+ Capybara.default_wait_time = 1
459
+ example.run
460
+ Capybara.default_wait_time = default_wait_time
461
+ end
462
+
463
+ it "waits for an element to appear in the viewport when clicked" do
464
+ subject.execute_script <<-JS
465
+ setTimeout(function() {
466
+ var offscreen = document.getElementById('offscreen')
467
+ offscreen.style.left = '10px';
468
+ }, 400);
469
+ JS
470
+
471
+ lambda { subject.click_link "Click Me" }.should_not raise_error(Capybara::Webkit::ClickFailed)
472
+ end
473
+ end
474
+ end
231
475
  end
@@ -9,6 +9,7 @@ describe Capybara::Webkit, 'compatibility with selenium' do
9
9
  <form onsubmit="return false">
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
+ <label for="three">Three</label><input type="text" name="three" id="three" readonly="readonly" />
12
13
  <input type="submit" value="Submit" id="submit" />
13
14
  </form>
14
15
  <script type="text/javascript">
@@ -18,7 +19,7 @@ describe Capybara::Webkit, 'compatibility with selenium' do
18
19
  };
19
20
  var elements = document.getElementsByTagName("input");
20
21
  var events = ["mousedown", "mouseup", "click", "keyup", "keydown",
21
- "keypress", "focus", "blur"];
22
+ "keypress", "focus", "blur", "input", "change"];
22
23
  for (var i = 0; i < elements.length; i++) {
23
24
  for (var j = 0; j < events.length; j++) {
24
25
  elements[i].addEventListener(events[j], recordEvent);
@@ -33,6 +34,7 @@ describe Capybara::Webkit, 'compatibility with selenium' do
33
34
  fill_in "One", :with => "some value"
34
35
  fill_in "One", :with => "a new value"
35
36
  fill_in "Two", :with => "other value"
37
+ fill_in "Three", :with => "readonly value"
36
38
  click_button "Submit"
37
39
  end
38
40
  end
data/spec/spec_helper.rb CHANGED
@@ -9,14 +9,6 @@ $LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
9
9
 
10
10
  Dir[File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')].each { |file| require(file) }
11
11
 
12
- spec_dir = nil
13
- $:.detect do |dir|
14
- if File.exists? File.join(dir, "capybara.rb")
15
- spec_dir = File.expand_path(File.join(dir,"..","spec"))
16
- $:.unshift( spec_dir )
17
- end
18
- end
19
-
20
12
  require 'capybara/webkit'
21
13
  connection = Capybara::Webkit::Connection.new(:socket_class => TCPSocket)
22
14
  $webkit_browser = Capybara::Webkit::Browser.new(connection)
@@ -25,7 +17,7 @@ if ENV['DEBUG']
25
17
  $webkit_browser.enable_logging
26
18
  end
27
19
 
28
- require File.join(spec_dir, "spec_helper")
20
+ require 'capybara/spec/spec_helper'
29
21
 
30
22
  Capybara.register_driver :reusable_webkit do |app|
31
23
  Capybara::Webkit::Driver.new(app, :browser => $webkit_browser)
data/src/Authenticate.cpp CHANGED
@@ -1,6 +1,7 @@
1
1
  #include "Authenticate.h"
2
2
  #include "WebPage.h"
3
3
  #include "NetworkAccessManager.h"
4
+ #include "WebPageManager.h"
4
5
 
5
6
  Authenticate::Authenticate(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
6
7
  }
@@ -9,10 +10,10 @@ void Authenticate::start() {
9
10
  QString username = arguments()[0];
10
11
  QString password = arguments()[1];
11
12
 
12
- NetworkAccessManager* networkAccessManager = page()->networkAccessManager();
13
+ NetworkAccessManager* networkAccessManager = manager()->networkAccessManager();
13
14
  networkAccessManager->setUserName(username);
14
15
  networkAccessManager->setPassword(password);
15
16
 
16
- emitFinished(true);
17
+ finish(true);
17
18
  }
18
19
 
data/src/ClearCookies.cpp CHANGED
@@ -9,5 +9,5 @@ ClearCookies::ClearCookies(WebPageManager *manager, QStringList &arguments, QObj
9
9
  void ClearCookies::start()
10
10
  {
11
11
  manager()->cookieJar()->clearCookies();
12
- emitFinished(true);
12
+ finish(true);
13
13
  }
@@ -7,5 +7,5 @@ ClearPromptText::ClearPromptText(WebPageManager *manager, QStringList &arguments
7
7
  void ClearPromptText::start()
8
8
  {
9
9
  page()->setPromptText(QString());
10
- emitFinished(true);
10
+ finish(true);
11
11
  }
data/src/Command.cpp CHANGED
@@ -1,4 +1,5 @@
1
- #include "SocketCommand.h"
1
+ #include "Command.h"
2
+ #include "ErrorMessage.h"
2
3
 
3
4
  Command::Command(QObject *parent) : QObject(parent) {
4
5
  }
@@ -7,15 +8,18 @@ QString Command::toString() const {
7
8
  return metaObject()->className();
8
9
  }
9
10
 
10
- void Command::emitFinished(bool success) {
11
+ void Command::finish(bool success) {
11
12
  emit finished(new Response(success, this));
12
13
  }
13
14
 
14
- void Command::emitFinished(bool success, QString message) {
15
+ void Command::finish(bool success, QString message) {
15
16
  emit finished(new Response(success, message, this));
16
17
  }
17
18
 
18
- void Command::emitFinished(bool success, QByteArray message) {
19
+ void Command::finish(bool success, QByteArray message) {
19
20
  emit finished(new Response(success, message, this));
20
21
  }
21
22
 
23
+ void Command::finish(bool success, ErrorMessage *message) {
24
+ emit finished(new Response(success, message, this));
25
+ }
data/src/Command.h CHANGED
@@ -1,10 +1,12 @@
1
1
  #ifndef COMMAND_H
2
2
  #define COMMAND_H
3
3
 
4
- #include <QObject>
5
4
  #include "Response.h"
5
+ #include <QObject>
6
6
  #include <QString>
7
7
 
8
+ class ErrorMessage;
9
+
8
10
  class Command : public QObject {
9
11
  Q_OBJECT
10
12
 
@@ -14,9 +16,10 @@ class Command : public QObject {
14
16
  virtual QString toString() const;
15
17
 
16
18
  protected:
17
- void emitFinished(bool success);
18
- void emitFinished(bool success, QString message);
19
- void emitFinished(bool success, QByteArray message);
19
+ void finish(bool success);
20
+ void finish(bool success, QString message);
21
+ void finish(bool success, QByteArray message);
22
+ void finish(bool success, ErrorMessage *message);
20
23
 
21
24
  signals:
22
25
  void finished(Response *response);
@@ -1,8 +1,7 @@
1
1
  #include "CommandFactory.h"
2
2
  #include "NullCommand.h"
3
- #include "SocketCommand.h"
4
3
  #include "Visit.h"
5
- #include "Find.h"
4
+ #include "FindXpath.h"
6
5
  #include "Reset.h"
7
6
  #include "Node.h"
8
7
  #include "Evaluate.h"
@@ -38,6 +37,9 @@
38
37
  #include "JavascriptConfirmMessages.h"
39
38
  #include "JavascriptPromptMessages.h"
40
39
  #include "SetUrlBlacklist.h"
40
+ #include "Version.h"
41
+ #include "Title.h"
42
+ #include "FindCss.h"
41
43
 
42
44
  CommandFactory::CommandFactory(WebPageManager *manager, QObject *parent) : QObject(parent) {
43
45
  m_manager = manager;
@@ -63,7 +63,7 @@ void CommandParser::processArgument(const char *data) {
63
63
  }
64
64
 
65
65
  if (m_arguments.length() == m_argumentsExpected) {
66
- Command *command = m_commandFactory->createCommand(m_commandName.toAscii().constData(), m_arguments);
66
+ Command *command = m_commandFactory->createCommand(m_commandName.toLatin1().constData(), m_arguments);
67
67
  emit commandReady(command);
68
68
  reset();
69
69
  }