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
@@ -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
  }