capybara-webkit 0.8.0 → 0.9.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.
@@ -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