capybara 3.2.1 → 3.3.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +22 -1
  3. data/README.md +1 -1
  4. data/lib/capybara.rb +2 -0
  5. data/lib/capybara/driver/base.rb +5 -1
  6. data/lib/capybara/driver/node.rb +4 -0
  7. data/lib/capybara/helpers.rb +25 -0
  8. data/lib/capybara/minitest.rb +7 -1
  9. data/lib/capybara/minitest/spec.rb +8 -1
  10. data/lib/capybara/node/base.rb +3 -3
  11. data/lib/capybara/node/element.rb +52 -0
  12. data/lib/capybara/node/matchers.rb +33 -0
  13. data/lib/capybara/queries/selector_query.rb +4 -4
  14. data/lib/capybara/queries/style_query.rb +41 -0
  15. data/lib/capybara/rack_test/browser.rb +7 -1
  16. data/lib/capybara/rack_test/node.rb +4 -0
  17. data/lib/capybara/rspec/compound.rb +67 -65
  18. data/lib/capybara/rspec/features.rb +2 -4
  19. data/lib/capybara/rspec/matchers.rb +30 -10
  20. data/lib/capybara/selector.rb +9 -0
  21. data/lib/capybara/selector/css.rb +74 -1
  22. data/lib/capybara/selector/filters/base.rb +2 -1
  23. data/lib/capybara/selector/filters/expression_filter.rb +2 -1
  24. data/lib/capybara/selector/selector.rb +1 -1
  25. data/lib/capybara/selenium/driver.rb +34 -43
  26. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +35 -0
  27. data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +31 -0
  28. data/lib/capybara/selenium/node.rb +17 -20
  29. data/lib/capybara/selenium/nodes/marionette_node.rb +31 -0
  30. data/lib/capybara/server.rb +8 -29
  31. data/lib/capybara/server/checker.rb +38 -0
  32. data/lib/capybara/spec/public/test.js +5 -0
  33. data/lib/capybara/spec/session/all_spec.rb +4 -0
  34. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  35. data/lib/capybara/spec/session/click_button_spec.rb +10 -0
  36. data/lib/capybara/spec/session/click_link_spec.rb +11 -0
  37. data/lib/capybara/spec/session/fill_in_spec.rb +2 -0
  38. data/lib/capybara/spec/session/find_link_spec.rb +18 -0
  39. data/lib/capybara/spec/session/find_spec.rb +1 -0
  40. data/lib/capybara/spec/session/first_spec.rb +1 -0
  41. data/lib/capybara/spec/session/has_css_spec.rb +0 -6
  42. data/lib/capybara/spec/session/has_style_spec.rb +25 -0
  43. data/lib/capybara/spec/session/node_spec.rb +34 -0
  44. data/lib/capybara/spec/session/save_page_spec.rb +4 -1
  45. data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -1
  46. data/lib/capybara/spec/session/text_spec.rb +1 -0
  47. data/lib/capybara/spec/session/title_spec.rb +1 -0
  48. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  49. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  50. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -0
  51. data/lib/capybara/spec/session/window/window_spec.rb +20 -0
  52. data/lib/capybara/spec/session/window/windows_spec.rb +1 -0
  53. data/lib/capybara/spec/session/window/within_window_spec.rb +1 -0
  54. data/lib/capybara/spec/session/within_spec.rb +1 -0
  55. data/lib/capybara/spec/spec_helper.rb +3 -1
  56. data/lib/capybara/spec/test_app.rb +18 -0
  57. data/lib/capybara/spec/views/form.erb +8 -0
  58. data/lib/capybara/spec/views/tables.erb +1 -1
  59. data/lib/capybara/spec/views/with_html.erb +9 -2
  60. data/lib/capybara/spec/views/with_js.erb +4 -0
  61. data/lib/capybara/spec/views/with_namespace.erb +20 -0
  62. data/lib/capybara/version.rb +1 -1
  63. data/lib/capybara/window.rb +11 -0
  64. data/spec/css_splitter_spec.rb +38 -0
  65. data/spec/dsl_spec.rb +1 -1
  66. data/spec/minitest_spec.rb +7 -1
  67. data/spec/minitest_spec_spec.rb +8 -1
  68. data/spec/rack_test_spec.rb +10 -0
  69. data/spec/rspec/shared_spec_matchers.rb +2 -0
  70. data/spec/selenium_spec_chrome.rb +28 -0
  71. data/spec/selenium_spec_chrome_remote.rb +2 -2
  72. data/spec/selenium_spec_marionette.rb +21 -1
  73. data/spec/server_spec.rb +0 -1
  74. data/spec/shared_selenium_session.rb +16 -1
  75. metadata +18 -19
@@ -51,6 +51,17 @@ Capybara::SpecHelper.spec "node" do
51
51
  end
52
52
  end
53
53
 
54
+ describe "#style", requires: [:css] do
55
+ it "should return the computed style value" do
56
+ expect(@session.find(:css, '#first').style('display')).to eq('display' => 'block')
57
+ expect(@session.find(:css, '#second').style(:display)).to eq('display' => 'inline')
58
+ end
59
+
60
+ it "should return multiple style values" do
61
+ expect(@session.find(:css, '#first').style('display', :'line-height')).to eq('display' => 'block', 'line-height' => '25px')
62
+ end
63
+ end
64
+
54
65
  describe "#value" do
55
66
  it "should allow retrieval of the value" do
56
67
  expect(@session.find('//textarea[@id="normal"]').value).to eq('banana')
@@ -185,6 +196,7 @@ Capybara::SpecHelper.spec "node" do
185
196
  expect(@session.find('//option', text: 'Disabled Child Option')).to be_disabled
186
197
  end
187
198
  end
199
+
188
200
  it "should be boolean" do
189
201
  @session.visit('/form')
190
202
  expect(@session.find('//select[@id="form_disabled_select"]/option').disabled?).to be true
@@ -370,6 +382,12 @@ Capybara::SpecHelper.spec "node" do
370
382
  expect(locations[:x].to_f).to be_within(1).of(5)
371
383
  expect(locations[:y].to_f).to be_within(1).of(5)
372
384
  end
385
+
386
+ it "should be able to click a table row", requires: [:js] do
387
+ @session.visit('/tables')
388
+ tr = @session.find(:css, '#agent_table tr:first-child').click
389
+ expect(tr).to have_css('label', text: 'Clicked')
390
+ end
373
391
  end
374
392
 
375
393
  describe '#double_click', requires: [:js] do
@@ -497,9 +515,25 @@ Capybara::SpecHelper.spec "node" do
497
515
  end
498
516
  end
499
517
 
518
+ describe "#evaluate_async_script", requires: %i[js es_args] do
519
+ it "should evaluate the given script in the context of the element" do
520
+ @session.visit('/with_js')
521
+ el = @session.find(:css, '#with_change_event')
522
+ expect(el.evaluate_async_script("arguments[0](this.value)")).to eq('default value')
523
+ end
524
+
525
+ it "should support returning elements after asynchronous operation" do
526
+ @session.visit('/with_js')
527
+ change = @session.find(:css, '#change') # ensure page has loaded and element is available
528
+ el = change.evaluate_async_script("var cb = arguments[0]; setTimeout(function(el){ cb(el) }, 100, this)")
529
+ expect(el).to eq(change)
530
+ end
531
+ end
532
+
500
533
  describe '#reload', requires: [:js] do
501
534
  context "without automatic reload" do
502
535
  before { Capybara.automatic_reload = false }
536
+
503
537
  it "should reload the current context of the node" do
504
538
  @session.visit('/with_js')
505
539
  node = @session.find(:css, '#reload-me')
@@ -3,11 +3,13 @@
3
3
  Capybara::SpecHelper.spec '#save_page' do
4
4
  let(:alternative_path) { File.join(Dir.pwd, "save_and_open_page_tmp") }
5
5
  before do
6
+ @old_save_path = Capybara.save_path
7
+ Capybara.save_path = nil
6
8
  @session.visit("/foo")
7
9
  end
8
10
 
9
11
  after do
10
- Capybara.save_path = nil
12
+ Capybara.save_path = @old_save_path
11
13
  Dir.glob("capybara-*.html").each do |file|
12
14
  FileUtils.rm(file)
13
15
  end
@@ -67,6 +69,7 @@ Capybara::SpecHelper.spec '#save_page' do
67
69
 
68
70
  context "asset_host contains a string" do
69
71
  before { Capybara.asset_host = "http://example.com" }
72
+
70
73
  after { Capybara.asset_host = nil }
71
74
 
72
75
  it "prepends base tag with value from asset_host to the head" do
@@ -3,11 +3,13 @@
3
3
  Capybara::SpecHelper.spec '#save_screenshot', requires: [:screenshot] do
4
4
  let(:alternative_path) { File.join(Dir.pwd, "save_screenshot_tmp") }
5
5
  before do
6
+ @old_save_path = Capybara.save_path
7
+ Capybara.save_path = nil
6
8
  @session.visit '/foo'
7
9
  end
8
10
 
9
11
  after do
10
- Capybara.save_path = nil
12
+ Capybara.save_path = @old_save_path
11
13
  FileUtils.rm_rf alternative_path
12
14
  end
13
15
 
@@ -44,6 +44,7 @@ Capybara::SpecHelper.spec '#text' do
44
44
 
45
45
  context "with css as default selector" do
46
46
  before { Capybara.default_selector = :css }
47
+
47
48
  it "should print the text of the page" do
48
49
  @session.visit('/with_simple_html')
49
50
  expect(@session.text).to eq('Bar')
@@ -8,6 +8,7 @@ Capybara::SpecHelper.spec '#title' do
8
8
 
9
9
  context "with css as default selector" do
10
10
  before { Capybara.default_selector = :css }
11
+
11
12
  it "should get the title of the page" do
12
13
  @session.visit('/with_title')
13
14
  expect(@session.title).to eq('Test Title')
@@ -5,6 +5,7 @@ Capybara::SpecHelper.spec '#current_window', requires: [:windows] do
5
5
  @window = @session.current_window
6
6
  @session.visit('/with_windows')
7
7
  end
8
+
8
9
  after do
9
10
  (@session.windows - [@window]).each do |w|
10
11
  @session.switch_to_window w
@@ -5,6 +5,7 @@ Capybara::SpecHelper.spec '#open_new_window', requires: [:windows] do
5
5
  @window = @session.current_window
6
6
  @session.visit('/with_windows')
7
7
  end
8
+
8
9
  after do
9
10
  (@session.windows - [@window]).each do |w|
10
11
  @session.switch_to_window w
@@ -6,6 +6,7 @@ Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do
6
6
  @session.visit('/with_windows')
7
7
  @session.assert_selector(:css, 'body.loaded')
8
8
  end
9
+
9
10
  after do
10
11
  (@session.windows - [@window]).each do |w|
11
12
  @session.switch_to_window w
@@ -5,6 +5,7 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
5
5
  @window = @session.current_window
6
6
  @session.visit('/with_windows')
7
7
  end
8
+
8
9
  after do
9
10
  (@session.windows - [@window]).each do |w|
10
11
  @session.switch_to_window w
@@ -112,6 +113,7 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
112
113
  before do
113
114
  @initial_size = @session.current_window.size
114
115
  end
116
+
115
117
  after do
116
118
  @session.current_window.resize_to(*@initial_size)
117
119
  sleep 0.5
@@ -143,6 +145,7 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
143
145
  before do
144
146
  @initial_size = @session.current_window.size
145
147
  end
148
+
146
149
  after do
147
150
  @session.current_window.resize_to(*@initial_size)
148
151
  sleep 0.5
@@ -181,4 +184,21 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
181
184
  expect(ow_height).to be > 300
182
185
  end
183
186
  end
187
+
188
+ describe '#fullscreen' do
189
+ before do
190
+ @initial_size = @session.current_window.size
191
+ end
192
+
193
+ after do
194
+ @session.current_window.resize_to(*@initial_size)
195
+ sleep 0.5
196
+ end
197
+
198
+ it "should be able to fullscreen the window" do
199
+ expect do
200
+ @session.current_window.fullscreen
201
+ end.not_to raise_error
202
+ end
203
+ end
184
204
  end
@@ -10,6 +10,7 @@ Capybara::SpecHelper.spec '#windows', requires: [:windows] do
10
10
  raise Capybara::CapybaraError if @session.windows.size != 3
11
11
  end
12
12
  end
13
+
13
14
  after do
14
15
  (@session.windows - [@window]).each(&:close)
15
16
  @session.switch_to_window(@window)
@@ -10,6 +10,7 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do
10
10
  raise Capybara::CapybaraError if @session.windows.size != 3
11
11
  end
12
12
  end
13
+
13
14
  after do
14
15
  (@session.windows - [@window]).each do |w|
15
16
  @session.switch_to_window w
@@ -59,6 +59,7 @@ Capybara::SpecHelper.spec '#within' do
59
59
 
60
60
  context "with the default selector set to CSS" do
61
61
  before { Capybara.default_selector = :css }
62
+
62
63
  it "should use CSS" do
63
64
  @session.within("#for_bar li", text: 'With Simple HTML') do
64
65
  @session.click_link('Go')
@@ -7,6 +7,8 @@ require "capybara/rspec" # Required here instead of in rspec_spec to avoid RSpec
7
7
  require "capybara/spec/test_app"
8
8
  require "nokogiri"
9
9
 
10
+ Capybara.save_path = File.join(Dir.pwd, 'save_path_tmp')
11
+
10
12
  module Capybara
11
13
  module SpecHelper
12
14
  class << self
@@ -15,7 +17,7 @@ module Capybara
15
17
  config.before { Capybara::SpecHelper.reset! }
16
18
  config.after { Capybara::SpecHelper.reset! }
17
19
  # Test in 3.5+ where metadata doesn't autotrigger shared context inclusion - will be only behavior in RSpec 4
18
- config.shared_context_metadata_behavior = :apply_to_host_groups if RSpec::Core::Version::STRING.to_f >= 3.5
20
+ config.shared_context_metadata_behavior = :apply_to_host_groups if config.respond_to?(:shared_context_metadata_behavior=)
19
21
  end
20
22
 
21
23
  def reset!
@@ -38,6 +38,14 @@ class TestApp < Sinatra::Base
38
38
  redirect '/landed'
39
39
  end
40
40
 
41
+ post '/redirect_307' do
42
+ redirect '/landed', 307
43
+ end
44
+
45
+ post '/redirect_308' do
46
+ redirect '/landed', 308
47
+ end
48
+
41
49
  get '/referer_base' do
42
50
  '<a href="/get_referer">direct link</a>' \
43
51
  '<a href="/redirect_to_get_referer">link via redirect</a>' \
@@ -69,6 +77,10 @@ class TestApp < Sinatra::Base
69
77
  "You landed"
70
78
  end
71
79
 
80
+ post '/landed' do
81
+ "You post landed: #{params.dig(:form, 'data')}"
82
+ end
83
+
72
84
  get '/with-quotes' do
73
85
  %q("No," he said, "you can't do that.")
74
86
  end
@@ -153,6 +165,12 @@ class TestApp < Sinatra::Base
153
165
  HTML
154
166
  end
155
167
 
168
+ get '/download.csv' do
169
+ content_type "text/csv"
170
+ 'This, is, comma, separated' \
171
+ 'Thomas, Walpole, was , here'
172
+ end
173
+
156
174
  get '/:view' do |view|
157
175
  erb view.to_sym, locals: { referrer: request.referrer }
158
176
  end
@@ -552,6 +552,14 @@ New line after and before textarea tag
552
552
  <p>
553
553
  <input type="submit" value="Go FAR"/>
554
554
  </p>
555
+
556
+ <input type="hidden" name="form[data]" value="TWTW"/>
557
+ <p>
558
+ <button formaction='/redirect_307'>Go 307</button>
559
+ </p>
560
+ <p>
561
+ <button formaction='/redirect_308'>Go 308</button>
562
+ </p>
555
563
  </form>
556
564
 
557
565
  <form action="/form" method="post">
@@ -3,7 +3,7 @@
3
3
  <table id="agent_table">
4
4
  <caption>Agent</caption>
5
5
 
6
- <tr>
6
+ <tr onclick="this.querySelector('td label').innerHTML = 'Clicked'">
7
7
  <td>
8
8
  <label for="form_agent_name">Name</label>
9
9
  </td>
@@ -1,4 +1,8 @@
1
1
 
2
+ <style>
3
+ p { display: block; }
4
+ </style>
5
+
2
6
  <div id="referrer"><%= referrer %></div>
3
7
  <h1>This is a test</h1>
4
8
 
@@ -13,7 +17,7 @@
13
17
  <span class="number">42</span>
14
18
  <span>Other span</span>
15
19
 
16
- <p class="para" id="first" data-random="abc\def">
20
+ <p class="para" id="first" data-random="abc\def" style="line-height: 25px;">
17
21
  Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
18
22
  tempor incididunt ut <a href="/with_simple_html" title="awesome title" class="simple">labore</a>
19
23
  et dolore magna aliqua. Ut enim ad minim veniam,
@@ -22,7 +26,7 @@
22
26
  <a href="/with_simple_html" aria-label="Go to simple"><img id="first_image" width="20" height="20" alt="awesome image" /></a>
23
27
  </p>
24
28
 
25
- <p class="para" id="second">
29
+ <p class="para" id="second" style="display: inline;">
26
30
  Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
27
31
  dolore eu fugiat <a href="/redirect" id="red">Redirect</a> pariatur. Excepteur sint occaecat cupidatat non proident,
28
32
  sunt in culpa qui officia
@@ -173,3 +177,6 @@ banana</textarea>
173
177
  <div id="ws">
174
178
  &#x20;&#x1680;&#x2000;&#x2001;&#x2002; &#x2003;&#x2004;&nbsp;&#x2005; &#x2006;&#x2007;&#x2008;&#x2009;&#x200A;&#x202F;&#x205F;&#x3000;
175
179
  </div>
180
+
181
+ <a href="/download.csv" download>Download Me</a>
182
+ <a href="/download.csv" download="other.csv">Download Other</a>
@@ -72,6 +72,10 @@
72
72
  <a href="#" id="change-title">Change title</a>
73
73
  </p>
74
74
 
75
+ <p>
76
+ <a href="#" id="change-size">Change size</a>
77
+ </p>
78
+
75
79
  <p id="click-test">Click me</p>
76
80
 
77
81
  <p>
@@ -0,0 +1,20 @@
1
+ <html xmlns="http://www.w3.org/1999/xhtml">
2
+ <head>
3
+ <title>Namespace</title>
4
+ </head>
5
+ <body id="body">
6
+ <div>
7
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1"
8
+ viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice"
9
+ style="width:100%; height:100%; position:absolute; top:0; left:0; z-index:-1;">
10
+ <linearGradient id="gradient">
11
+ <stop class="begin" offset="0%"/>
12
+ <stop class="end" offset="100%"/>
13
+ </linearGradient>
14
+ <rect x="0" y="0" width="100" height="100" style="fill:url(#gradient)" />
15
+ <rect x="150" y="150" width="25" height="25" style="fill:url(#gradient)" />
16
+ <circle cx="50" cy="50" r="30" style="fill:url(#gradient)" />
17
+ </svg>
18
+ <div>
19
+ </body>
20
+ </html>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Capybara
4
- VERSION = '3.2.1'
4
+ VERSION = '3.3.0'
5
5
  end
@@ -101,6 +101,17 @@ module Capybara
101
101
  wait_for_stable_size { @driver.maximize_window(handle) }
102
102
  end
103
103
 
104
+ ##
105
+ # Fullscreen window.
106
+ #
107
+ # If a particular driver doesn't have concept of fullscreen it may not support this method.
108
+ #
109
+ # @macro about_current
110
+ #
111
+ def fullscreen
112
+ @driver.fullscreen_window(handle)
113
+ end
114
+
104
115
  def eql?(other)
105
116
  other.is_a?(self.class) && @session == other.session && @handle == other.handle
106
117
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Capybara::Selector::CSS::Splitter do
6
+ let :splitter do
7
+ ::Capybara::Selector::CSS::Splitter.new
8
+ end
9
+
10
+ context "split not needed" do
11
+ it "normal CSS selector" do
12
+ css = 'div[id="abc"]'
13
+ expect(splitter.split(css)).to eq [css]
14
+ end
15
+
16
+ it "comma in strings" do
17
+ css = 'div[id="a,bc"]'
18
+ expect(splitter.split(css)).to eq [css]
19
+ end
20
+
21
+ it "comma in pseudo-selector" do
22
+ css = 'div.class1:not(.class1, .class2)'
23
+ expect(splitter.split(css)).to eq [css]
24
+ end
25
+ end
26
+
27
+ context "split needed" do
28
+ it "root level comma" do
29
+ css = 'div.class1, span, p.class2'
30
+ expect(splitter.split(css)).to eq ['div.class1', 'span', 'p.class2']
31
+ end
32
+
33
+ it "root level comma when quotes and pseudo selectors" do
34
+ css = 'div.class1[id="abc\\"def,ghi"]:not(.class3, .class4), span[id=\'a"c\\\'de\'], section, #abc\\,def'
35
+ expect(splitter.split(css)).to eq ['div.class1[id="abc\\"def,ghi"]:not(.class3, .class4)', 'span[id=\'a"c\\\'de\']', 'section', '#abc\\,def']
36
+ end
37
+ end
38
+ end
@@ -8,7 +8,7 @@ class TestClass
8
8
  end
9
9
 
10
10
  Capybara::SpecHelper.run_specs TestClass.new, "DSL", capybara_skip: %i[
11
- js modals screenshot frames windows send_keys server hover about_scheme psc
11
+ js modals screenshot frames windows send_keys server hover about_scheme psc download css
12
12
  ]
13
13
 
14
14
  RSpec.describe Capybara::DSL do
@@ -112,6 +112,12 @@ class MinitestTest < Minitest::Test
112
112
  assert_matches_xpath(find(:select, 'form_title'), './/select[@id="form_title"]')
113
113
  refute_matches_xpath(find(:select, 'form_title'), './/select[@id="form_other_title"]')
114
114
  end
115
+
116
+ def test_assert_style
117
+ skip "Rack test doesn't support style" if Capybara.current_driver == :rack_test
118
+ visit('/with_html')
119
+ assert_style(find(:css, '#second'), display: 'inline')
120
+ end
115
121
  end
116
122
 
117
123
  RSpec.describe 'capybara/minitest' do
@@ -126,6 +132,6 @@ RSpec.describe 'capybara/minitest' do
126
132
  reporter.start
127
133
  MinitestTest.run reporter, {}
128
134
  reporter.report
129
- expect(output.string).to include("17 runs, 44 assertions, 0 failures, 0 errors, 0 skips")
135
+ expect(output.string).to include("18 runs, 44 assertions, 0 failures, 0 errors, 1 skips")
130
136
  end
131
137
  end