capybara-webkit 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,14 @@
1
1
  We love pull requests. Here's a quick guide:
2
2
 
3
+ Dependencies
4
+
5
+ Some of the tests depend on the `identify` command that comes with Imagemagick.
6
+ Imagemagick can be installed via [homebrew](http://mxcl.github.com/homebrew/).
7
+
8
+ brew install imagemagick
9
+
10
+ Contributing
11
+
3
12
  1. Fork the repo.
4
13
 
5
14
  2. Run the tests. We only take pull requests with passing tests, and it's great
@@ -0,0 +1,18 @@
1
+ 2012-02-01 Joe Ferris <jferris@thoughtbot.com>
2
+ * driver_spec.rb, Connection.cpp, Connection.h:
3
+ Try to detect when a command starts a page load and wait for it to finish
4
+
5
+ 2012-01-27 Matthew Mongeau <halogenandtoast@gmail.com>
6
+ * driver_spec.rb, capybara.js: Trigger mousedown and mouseup events
7
+ * driver_spec.rb, capybara.js: Simulate browser events more closely
8
+
9
+ 2012-01-19 Marco Antonio <marcofognog@gmail.com>
10
+ * node.rb, driver_spec.rb:
11
+ Raise ElementNotDisplayedError also for #drag_to and #select_option when they are invisible.
12
+
13
+ 2012-01-18 Marco Antonio <marcofognog@gmail.com>
14
+ * node.rb, driver_spec.rb:
15
+ Raise error when an invisible element receives #click so it resembles a browser more closely.
16
+
17
+ 2012-01-15 Marc Schwieterman
18
+ * CONTRIBUTING.md: add imagemagick dependency to contributing guide
data/NEWS.md ADDED
@@ -0,0 +1,7 @@
1
+ New for 0.9.0:
2
+
3
+ * Raise an error when an invisible element receives #click.
4
+ * Raise ElementNotDisplayedError for #drag_to and #select_option when element is invisible.
5
+ * Trigger mousedown and mouseup events.
6
+ * Model mouse events more closely to the browser.
7
+ * Try to detech when a command starts a page load and wait for it to finish
@@ -1,5 +1,9 @@
1
1
  class Capybara::Driver::Webkit
2
2
  class Node < Capybara::Driver::Node
3
+
4
+ class ElementNotDisplayedError < StandardError
5
+ end
6
+
3
7
  NBSP = "\xC2\xA0"
4
8
  NBSP.force_encoding("UTF-8") if NBSP.respond_to?(:force_encoding)
5
9
 
@@ -33,6 +37,7 @@ class Capybara::Driver::Webkit
33
37
  end
34
38
 
35
39
  def select_option
40
+ check_visibility(self)
36
41
  invoke "selectOption"
37
42
  end
38
43
 
@@ -46,10 +51,13 @@ class Capybara::Driver::Webkit
46
51
  end
47
52
 
48
53
  def click
54
+ check_visibility(self)
49
55
  invoke "click"
50
56
  end
51
57
 
52
58
  def drag_to(element)
59
+ check_visibility(self)
60
+ check_visibility(element)
53
61
  invoke 'dragTo', element.native
54
62
  end
55
63
 
@@ -118,5 +126,9 @@ class Capybara::Driver::Webkit
118
126
  def multiple_select?
119
127
  self.tag_name == "select" && self["multiple"] == "multiple"
120
128
  end
129
+
130
+ def check_visibility(element)
131
+ raise(ElementNotDisplayedError, "This element is not visible so it may not be interacted with") unless element.visible?
132
+ end
121
133
  end
122
134
  end
@@ -1,7 +1,7 @@
1
1
  module Capybara
2
2
  module Driver
3
3
  class Webkit
4
- VERSION = '0.8.0'.freeze
4
+ VERSION = '0.9.0'.freeze
5
5
  end
6
6
  end
7
7
  end
@@ -529,6 +529,44 @@ describe Capybara::Driver::Webkit do
529
529
  end
530
530
  end
531
531
 
532
+ context "dom events" do
533
+ before(:all) do
534
+ @app = lambda do |env|
535
+ body = <<-HTML
536
+
537
+ <html><body>
538
+ <a href='#' class='watch'>Link</a>
539
+ <ul id="events"></ul>
540
+ <script type="text/javascript">
541
+ var events = document.getElementById("events");
542
+ var recordEvent = function (event) {
543
+ var element = document.createElement("li");
544
+ element.innerHTML = event.type;
545
+ events.appendChild(element);
546
+ };
547
+
548
+ var elements = document.getElementsByClassName("watch");
549
+ for (var i = 0; i < elements.length; i++) {
550
+ var element = elements[i];
551
+ element.addEventListener("mousedown", recordEvent);
552
+ element.addEventListener("mouseup", recordEvent);
553
+ element.addEventListener("click", recordEvent);
554
+ }
555
+ </script>
556
+ </body></html>
557
+ HTML
558
+ [200,
559
+ { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
560
+ [body]]
561
+ end
562
+ end
563
+
564
+ it "triggers mouse events" do
565
+ subject.find("//a").first.click
566
+ subject.find("//li").map(&:text).should == %w(mousedown mouseup click)
567
+ end
568
+ end
569
+
532
570
  context "form events app" do
533
571
  before(:all) do
534
572
  @app = lambda do |env|
@@ -564,6 +602,8 @@ describe Capybara::Driver::Webkit do
564
602
  element.addEventListener("keyup", recordEvent);
565
603
  element.addEventListener("change", recordEvent);
566
604
  element.addEventListener("blur", recordEvent);
605
+ element.addEventListener("mousedown", recordEvent);
606
+ element.addEventListener("mouseup", recordEvent);
567
607
  element.addEventListener("click", recordEvent);
568
608
  }
569
609
  </script>
@@ -597,12 +637,12 @@ describe Capybara::Driver::Webkit do
597
637
 
598
638
  it "triggers radio input events" do
599
639
  subject.find("//input[@type='radio']").first.set(true)
600
- subject.find("//li").map(&:text).should == %w(click change)
640
+ subject.find("//li").map(&:text).should == %w(mousedown mouseup change click)
601
641
  end
602
642
 
603
643
  it "triggers checkbox events" do
604
644
  subject.find("//input[@type='checkbox']").first.set(true)
605
- subject.find("//li").map(&:text).should == %w(click change)
645
+ subject.find("//li").map(&:text).should == %w(mousedown mouseup change click)
606
646
  end
607
647
  end
608
648
 
@@ -614,10 +654,13 @@ describe Capybara::Driver::Webkit do
614
654
  <div id="change">Change me</div>
615
655
  <div id="mouseup">Push me</div>
616
656
  <div id="mousedown">Release me</div>
657
+ <div id="invisible-mouseup" style="display:none;">You can't push me</div>
658
+ <div id="invisible-mousedown" style="display:none;">You can't release me</div>
617
659
  <form action="/" method="GET">
618
660
  <select id="change_select" name="change_select">
619
661
  <option value="1" id="option-1" selected="selected">one</option>
620
662
  <option value="2" id="option-2">two</option>
663
+ <option value="2" id="invisible-option" style="display:none;">three</option>
621
664
  </select>
622
665
  </form>
623
666
  <script type="text/javascript">
@@ -639,6 +682,7 @@ describe Capybara::Driver::Webkit do
639
682
  });
640
683
  </script>
641
684
  <a href="/next">Next</a>
685
+ <a href="/next" id="hidden" style="display:none;">Not displayed</a>
642
686
  </body></html>
643
687
  HTML
644
688
  [200,
@@ -647,7 +691,7 @@ describe Capybara::Driver::Webkit do
647
691
  end
648
692
  end
649
693
 
650
- it "clicks an element" do
694
+ it "clicks a visible element" do
651
695
  subject.find("//a").first.click
652
696
  subject.current_url =~ %r{/next$}
653
697
  end
@@ -679,6 +723,39 @@ describe Capybara::Driver::Webkit do
679
723
 
680
724
  subject.find("//*[@class='triggered']").size.should == 1
681
725
  end
726
+
727
+ context "raises error when" do
728
+ it "tries to click an invisible element" do
729
+ expect {
730
+ subject.find("//*[@id='hidden']").first.click
731
+ }.to raise_error(Capybara::Driver::Webkit::Node::ElementNotDisplayedError)
732
+ end
733
+
734
+ it "tries to drag an invisible element to a visible one" do
735
+ draggable = subject.find("//*[@id='invisible-mousedown']").first
736
+ container = subject.find("//*[@id='mouseup']").first
737
+
738
+ expect {
739
+ draggable.drag_to(container)
740
+ }.to raise_error(Capybara::Driver::Webkit::Node::ElementNotDisplayedError)
741
+ end
742
+
743
+ it "tries to drag a visible element to an invisible one" do
744
+ draggable = subject.find("//*[@id='mousedown']").first
745
+ container = subject.find("//*[@id='invisible-mouseup']").first
746
+
747
+ expect {
748
+ draggable.drag_to(container)
749
+ }.to raise_error(Capybara::Driver::Webkit::Node::ElementNotDisplayedError)
750
+ end
751
+
752
+ it "tries to select an invisible option" do
753
+ option = subject.find("//option[@id='invisible-option']").first
754
+ expect {
755
+ option.select_option
756
+ }.to raise_error(Capybara::Driver::Webkit::Node::ElementNotDisplayedError)
757
+ end
758
+ end
682
759
  end
683
760
 
684
761
  context "nesting app" do
@@ -706,14 +783,13 @@ describe Capybara::Driver::Webkit do
706
783
 
707
784
  context "slow app" do
708
785
  before(:all) do
786
+ @result = ""
709
787
  @app = lambda do |env|
710
- body = <<-HTML
711
- <html><body>
712
- <form action="/next"><input type="submit"/></form>
713
- <p>#{env['PATH_INFO']}</p>
714
- </body></html>
715
- HTML
716
- sleep(0.5)
788
+ if env["PATH_INFO"] == "/result"
789
+ sleep(0.5)
790
+ @result << "finished"
791
+ end
792
+ body = %{<html><body><a href="/result">Go</a></body></html>}
717
793
  [200,
718
794
  { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
719
795
  [body]]
@@ -721,8 +797,8 @@ describe Capybara::Driver::Webkit do
721
797
  end
722
798
 
723
799
  it "waits for a request to load" do
724
- subject.find("//input").first.click
725
- subject.find("//p").first.text.should == "/next"
800
+ subject.find("//a").first.click
801
+ @result.should == "finished"
726
802
  end
727
803
  end
728
804
 
@@ -17,6 +17,8 @@ Connection::Connection(QTcpSocket *socket, WebPage *page, QObject *parent) :
17
17
  m_command = NULL;
18
18
  m_pageSuccess = true;
19
19
  m_commandWaiting = false;
20
+ m_pageLoadingFromCommand = false;
21
+ m_pendingResponse = NULL;
20
22
  connect(m_socket, SIGNAL(readyRead()), m_commandParser, SLOT(checkNext()));
21
23
  connect(m_commandParser, SIGNAL(commandReady(QString, QStringList)), this, SLOT(commandReady(QString, QStringList)));
22
24
  connect(m_page, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
@@ -38,6 +40,7 @@ void Connection::startCommand() {
38
40
  if (m_pageSuccess) {
39
41
  m_command = m_commandFactory->createCommand(m_commandName.toAscii().constData());
40
42
  if (m_command) {
43
+ connect(m_page, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
41
44
  connect(m_command,
42
45
  SIGNAL(finished(Response *)),
43
46
  this,
@@ -55,16 +58,31 @@ void Connection::startCommand() {
55
58
  }
56
59
  }
57
60
 
61
+ void Connection::pageLoadingFromCommand() {
62
+ m_pageLoadingFromCommand = true;
63
+ }
64
+
58
65
  void Connection::pendingLoadFinished(bool success) {
59
66
  m_pageSuccess = success;
60
67
  if (m_commandWaiting)
61
68
  startCommand();
69
+ if (m_pageLoadingFromCommand) {
70
+ m_pageLoadingFromCommand = false;
71
+ if (m_pendingResponse) {
72
+ writeResponse(m_pendingResponse);
73
+ m_pendingResponse = NULL;
74
+ }
75
+ }
62
76
  }
63
77
 
64
78
  void Connection::finishCommand(Response *response) {
79
+ disconnect(m_page, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
65
80
  m_command->deleteLater();
66
81
  m_command = NULL;
67
- writeResponse(response);
82
+ if (m_pageLoadingFromCommand)
83
+ m_pendingResponse = response;
84
+ else
85
+ writeResponse(response);
68
86
  }
69
87
 
70
88
  void Connection::writeResponse(Response *response) {
@@ -18,6 +18,7 @@ class Connection : public QObject {
18
18
  void commandReady(QString commandName, QStringList arguments);
19
19
  void finishCommand(Response *response);
20
20
  void pendingLoadFinished(bool success);
21
+ void pageLoadingFromCommand();
21
22
 
22
23
  private:
23
24
  void startCommand();
@@ -32,5 +33,7 @@ class Connection : public QObject {
32
33
  CommandFactory *m_commandFactory;
33
34
  bool m_pageSuccess;
34
35
  bool m_commandWaiting;
36
+ bool m_pageLoadingFromCommand;
37
+ Response *m_pendingResponse;
35
38
  };
36
39
 
@@ -96,7 +96,21 @@ Capybara = {
96
96
  return this.nodes[index].submit();
97
97
  },
98
98
 
99
+ mousedown: function(index) {
100
+ var mousedownEvent = document.createEvent('MouseEvents');
101
+ mousedownEvent.initMouseEvent('mousedown', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
102
+ this.nodes[index].dispatchEvent(mousedownEvent);
103
+ },
104
+
105
+ mouseup: function(index) {
106
+ var mouseupEvent = document.createEvent('MouseEvents');
107
+ mouseupEvent.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
108
+ this.nodes[index].dispatchEvent(mouseupEvent);
109
+ },
110
+
99
111
  click: function (index) {
112
+ this.mousedown(index);
113
+ this.mouseup(index);
100
114
  var clickEvent = document.createEvent('MouseEvents');
101
115
  clickEvent.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
102
116
  this.nodes[index].dispatchEvent(clickEvent);
@@ -167,13 +181,13 @@ Capybara = {
167
181
  this.trigger(index, "blur");
168
182
 
169
183
  } else if (type === "checkbox" || type === "radio") {
170
- node.checked = (value === "true");
171
- this.trigger(index, "click");
172
- this.trigger(index, "change");
184
+ if (node.checked != (value === "true")) {
185
+ this.click(index)
186
+ }
173
187
 
174
188
  } else if (type === "file") {
175
189
  this.lastAttachedFile = value;
176
- this.trigger(index, "click");
190
+ this.click(index)
177
191
 
178
192
  } else {
179
193
  node.value = value;
metadata CHANGED
@@ -1,15 +1,10 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: capybara-webkit
3
- version: !ruby/object:Gem::Version
4
- hash: 63
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 8
9
- - 0
10
- version: 0.8.0
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - thoughtbot
14
9
  - Joe Ferris
15
10
  - Matt Mongeau
@@ -18,136 +13,104 @@ authors:
18
13
  autorequire:
19
14
  bindir: bin
20
15
  cert_chain: []
21
-
22
- date: 2012-01-20 00:00:00 Z
23
- dependencies:
24
- - !ruby/object:Gem::Dependency
25
- version_requirements: &id001 !ruby/object:Gem::Requirement
16
+ date: 2012-02-03 00:00:00.000000000Z
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: capybara
20
+ requirement: &2158944240 !ruby/object:Gem::Requirement
26
21
  none: false
27
- requirements:
28
- - - ">="
29
- - !ruby/object:Gem::Version
30
- hash: 23
31
- segments:
32
- - 1
33
- - 0
34
- - 0
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
35
25
  version: 1.0.0
36
26
  - - <
37
- - !ruby/object:Gem::Version
38
- hash: 11
39
- segments:
40
- - 1
41
- - 2
42
- version: "1.2"
43
- requirement: *id001
27
+ - !ruby/object:Gem::Version
28
+ version: '1.2'
44
29
  type: :runtime
45
30
  prerelease: false
46
- name: capybara
47
- - !ruby/object:Gem::Dependency
48
- version_requirements: &id002 !ruby/object:Gem::Requirement
31
+ version_requirements: *2158944240
32
+ - !ruby/object:Gem::Dependency
33
+ name: json
34
+ requirement: &2158943060 !ruby/object:Gem::Requirement
49
35
  none: false
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- hash: 3
54
- segments:
55
- - 0
56
- version: "0"
57
- requirement: *id002
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
58
40
  type: :runtime
59
41
  prerelease: false
60
- name: json
61
- - !ruby/object:Gem::Dependency
62
- version_requirements: &id003 !ruby/object:Gem::Requirement
42
+ version_requirements: *2158943060
43
+ - !ruby/object:Gem::Dependency
44
+ name: rspec
45
+ requirement: &2158941580 !ruby/object:Gem::Requirement
63
46
  none: false
64
- requirements:
47
+ requirements:
65
48
  - - ~>
66
- - !ruby/object:Gem::Version
67
- hash: 23
68
- segments:
69
- - 2
70
- - 6
71
- - 0
49
+ - !ruby/object:Gem::Version
72
50
  version: 2.6.0
73
- requirement: *id003
74
- type: :development
75
- prerelease: false
76
- name: rspec
77
- - !ruby/object:Gem::Dependency
78
- version_requirements: &id004 !ruby/object:Gem::Requirement
79
- none: false
80
- requirements:
81
- - - ">="
82
- - !ruby/object:Gem::Version
83
- hash: 3
84
- segments:
85
- - 0
86
- version: "0"
87
- requirement: *id004
88
51
  type: :development
89
52
  prerelease: false
53
+ version_requirements: *2158941580
54
+ - !ruby/object:Gem::Dependency
90
55
  name: sinatra
91
- - !ruby/object:Gem::Dependency
92
- version_requirements: &id005 !ruby/object:Gem::Requirement
56
+ requirement: &2158940900 !ruby/object:Gem::Requirement
93
57
  none: false
94
- requirements:
95
- - - ">="
96
- - !ruby/object:Gem::Version
97
- hash: 3
98
- segments:
99
- - 0
100
- version: "0"
101
- requirement: *id005
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
102
62
  type: :development
103
63
  prerelease: false
64
+ version_requirements: *2158940900
65
+ - !ruby/object:Gem::Dependency
104
66
  name: mini_magick
105
- - !ruby/object:Gem::Dependency
106
- version_requirements: &id006 !ruby/object:Gem::Requirement
67
+ requirement: &2158940440 !ruby/object:Gem::Requirement
107
68
  none: false
108
- requirements:
109
- - - ">="
110
- - !ruby/object:Gem::Version
111
- hash: 3
112
- segments:
113
- - 0
114
- version: "0"
115
- requirement: *id006
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
116
73
  type: :development
117
74
  prerelease: false
75
+ version_requirements: *2158940440
76
+ - !ruby/object:Gem::Dependency
118
77
  name: rake
119
- - !ruby/object:Gem::Dependency
120
- version_requirements: &id007 !ruby/object:Gem::Requirement
78
+ requirement: &2158939820 !ruby/object:Gem::Requirement
121
79
  none: false
122
- requirements:
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ type: :development
85
+ prerelease: false
86
+ version_requirements: *2158939820
87
+ - !ruby/object:Gem::Dependency
88
+ name: appraisal
89
+ requirement: &2158939100 !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
123
92
  - - ~>
124
- - !ruby/object:Gem::Version
125
- hash: 15
126
- segments:
127
- - 0
128
- - 4
129
- - 0
93
+ - !ruby/object:Gem::Version
130
94
  version: 0.4.0
131
- requirement: *id007
132
95
  type: :development
133
96
  prerelease: false
134
- name: appraisal
97
+ version_requirements: *2158939100
135
98
  description:
136
99
  email: support@thoughtbot.com
137
100
  executables: []
138
-
139
- extensions:
101
+ extensions:
140
102
  - extconf.rb
141
103
  extra_rdoc_files: []
142
-
143
- files:
104
+ files:
144
105
  - .gitignore
145
106
  - .rspec
146
107
  - Appraisals
147
108
  - CONTRIBUTING.md
109
+ - ChangeLog
148
110
  - Gemfile
149
111
  - Gemfile.lock
150
112
  - LICENSE
113
+ - NEWS.md
151
114
  - README.md
152
115
  - Rakefile
153
116
  - bin/Info.plist
@@ -247,38 +210,29 @@ files:
247
210
  - webkit_server.pro
248
211
  homepage: http://github.com/thoughtbot/capybara-webkit
249
212
  licenses: []
250
-
251
213
  post_install_message:
252
214
  rdoc_options: []
253
-
254
- require_paths:
215
+ require_paths:
255
216
  - lib
256
- required_ruby_version: !ruby/object:Gem::Requirement
217
+ required_ruby_version: !ruby/object:Gem::Requirement
257
218
  none: false
258
- requirements:
259
- - - ">="
260
- - !ruby/object:Gem::Version
261
- hash: 3
262
- segments:
263
- - 0
264
- version: "0"
265
- required_rubygems_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ! '>='
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ required_rubygems_version: !ruby/object:Gem::Requirement
266
224
  none: false
267
- requirements:
268
- - - ">="
269
- - !ruby/object:Gem::Version
270
- hash: 3
271
- segments:
272
- - 0
273
- version: "0"
225
+ requirements:
226
+ - - ! '>='
227
+ - !ruby/object:Gem::Version
228
+ version: '0'
274
229
  requirements: []
275
-
276
230
  rubyforge_project:
277
231
  rubygems_version: 1.8.11
278
232
  signing_key:
279
233
  specification_version: 3
280
234
  summary: Headless Webkit driver for Capybara
281
- test_files:
235
+ test_files:
282
236
  - spec/browser_spec.rb
283
237
  - spec/cookie_jar_spec.rb
284
238
  - spec/driver_rendering_spec.rb