capybara 2.15.1 → 2.15.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4eff01a08419c2e76c521c460193065f256f6fe4
4
- data.tar.gz: 588eeea7fc4088e2e470bd4d573056be00782248
3
+ metadata.gz: 4eb9fd96f4bd71cd4b6f610b7dae3d0b6da6fe21
4
+ data.tar.gz: 615cf0e0fb68f9d6792338470bdd9dd277c4c673
5
5
  SHA512:
6
- metadata.gz: 1281d86fc6e0ff8533bc4e9d0232adf51b9ce7c33a48a9eba5842e2b24271dbf909efab1c80e9b14ca7ca6861bc1f489d0cb094cce0f8d822f3a387f60c6672f
7
- data.tar.gz: d713cafa706d2ddbd23dc05524128fff444a2040d602d8352b23acd0e718c8bb2a844ca36a72d2decc7aa1016aaa8cd75d579754457d5260add9aa797d536c6a
6
+ metadata.gz: 48a0d0f5c133fbfd57a424410943830b1ce28cfe21784b4e085f9a2cfcf0f6bf207dca0cda824875a140aaf103d54c6e76ed3f821fe3700e33348bf1aaca9e22
7
+ data.tar.gz: a9b0cbed365316d646d66cce8c11912631bd9c2214e90eea6d81ebec289cec5e5a66428a57a6e85d68ee936e1a9570193fc7982b85e5bb89ac5461812523d631
data/History.md CHANGED
@@ -1,3 +1,13 @@
1
+ # Version 2.15.2
2
+ Release date: 2017-10-02
3
+
4
+ ### Fixed
5
+
6
+ * Include within scope description in element not found/ambiguous errors [Thomas Walpole]
7
+ * Raise error when no activation block is passed to modal methods if using headless chrome [Thomas Walpole]
8
+ * Don't retry element access when inspecting [Ivan Neverov]
9
+ * Don't override a specified port (even if it is default port) in visited url [Thomas Walpole]
10
+
1
11
  # Version 2.15.1
2
12
 
3
13
  Release date: 2017-08-04
data/README.md CHANGED
@@ -10,6 +10,12 @@ interact with your app. It is agnostic about the driver running your tests and
10
10
  comes with Rack::Test and Selenium support built in. WebKit is supported
11
11
  through an external gem.
12
12
 
13
+ ## Support Capybara
14
+
15
+ If you and/or your company find value in Capybara and would like to contribute financially to its ongoing maintenance and development, please visit
16
+ <a href="https://www.patreon.com/capybara">Patreon</a>
17
+
18
+
13
19
  **Need help?** Ask on the mailing list (please do not open an issue on
14
20
  GitHub): http://groups.google.com/group/ruby-capybara
15
21
 
@@ -215,7 +215,7 @@ module Capybara
215
215
  # Assertion that there is no checked_field
216
216
  #
217
217
  # @!method assert_no_checked_field
218
- # @!method refute_chceked_field
218
+ # @!method refute_checked_field
219
219
 
220
220
  ##
221
221
  # Assertion that there is unchecked_field
@@ -371,9 +371,9 @@ module Capybara
371
371
  end
372
372
 
373
373
  def inspect
374
- %(#<Capybara::Node::Element tag="#{tag_name}" path="#{path}">)
374
+ %(#<Capybara::Node::Element tag="#{base.tag_name}" path="#{base.path}">)
375
375
  rescue NotSupportedByDriverError
376
- %(#<Capybara::Node::Element tag="#{tag_name}">)
376
+ %(#<Capybara::Node::Element tag="#{base.tag_name}">)
377
377
  rescue => e
378
378
  if session.driver.invalid_element_errors.any? { |et| e.is_a?(et)}
379
379
  %(Obsolete #<Capybara::Node::Element>)
@@ -306,6 +306,7 @@ module Capybara
306
306
  else
307
307
  result = query.resolve_for(self)
308
308
  end
309
+
309
310
  if query.match == :one or query.match == :smart and result.size > 1
310
311
  raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
311
312
  end
@@ -4,7 +4,7 @@ module Capybara
4
4
  class AncestorQuery < MatchQuery
5
5
  # @api private
6
6
  def resolve_for(node, exact = nil)
7
- @resolved_node = node
7
+ @child_node = node
8
8
  node.synchronize do
9
9
  match_results = super(node.session.current_scope, exact)
10
10
  node.all(:xpath, XPath.ancestor) do |el|
@@ -15,7 +15,7 @@ module Capybara
15
15
 
16
16
  def description
17
17
  desc = super
18
- if @resolved_node && (child_query = @resolved_node.instance_variable_get(:@query))
18
+ if @child_node && (child_query = @child_node.instance_variable_get(:@query))
19
19
  desc += " that is an ancestor of #{child_query.description}"
20
20
  end
21
21
  desc
@@ -8,6 +8,7 @@ module Capybara
8
8
  VALID_MATCH = [:first, :smart, :prefer_exact, :one]
9
9
 
10
10
  def initialize(*args, &filter_block)
11
+ @resolved_node = nil
11
12
  @options = if args.last.is_a?(Hash) then args.pop.dup else {} end
12
13
  super(@options)
13
14
 
@@ -53,6 +54,7 @@ module Capybara
53
54
  @description << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
54
55
  @description << selector.description(options)
55
56
  @description << " that also matches the custom filter block" if @filter_block
57
+ @description << " within #{@resolved_node.inspect}" if describe_within?
56
58
  @description
57
59
  end
58
60
 
@@ -140,6 +142,7 @@ module Capybara
140
142
 
141
143
  # @api private
142
144
  def resolve_for(node, exact = nil)
145
+ @resolved_node = node
143
146
  node.synchronize do
144
147
  children = if selector.format == :css
145
148
  node.find_css(self.css)
@@ -239,6 +242,11 @@ module Capybara
239
242
  def exact_text
240
243
  options.fetch(:exact_text, session_options.exact_text)
241
244
  end
245
+
246
+ def describe_within?
247
+ @resolved_node && !(@resolved_node.is_a?(::Capybara::Node::Document) ||
248
+ (@resolved_node.is_a?(::Capybara::Node::Simple) && @resolved_node.path == '/'))
249
+ end
242
250
  end
243
251
  end
244
252
  end
@@ -4,7 +4,7 @@ module Capybara
4
4
  class SiblingQuery < MatchQuery
5
5
  # @api private
6
6
  def resolve_for(node, exact = nil)
7
- @resolved_node = node
7
+ @sibling_node = node
8
8
  node.synchronize do
9
9
  match_results = super(node.session.current_scope, exact)
10
10
  node.all(:xpath, XPath.preceding_sibling.union(XPath.following_sibling)) do |el|
@@ -15,8 +15,8 @@ module Capybara
15
15
 
16
16
  def description
17
17
  desc = super
18
- if @resolved_node && (child_query = @resolved_node.instance_variable_get(:@query))
19
- desc += " that is a sibling of #{child_query.description}"
18
+ if @sibling_node && (sibling_query = @sibling_node.instance_variable_get(:@query))
19
+ desc += " that is a sibling of #{sibling_query.description}"
20
20
  end
21
21
  desc
22
22
  end
@@ -245,8 +245,9 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
245
245
 
246
246
  def accept_modal(_type, options={})
247
247
  if headless_chrome?
248
+ raise ArgumentError, "Block that triggers the system modal is missing" unless block_given?
248
249
  insert_modal_handlers(true, options[:with], options[:text])
249
- yield if block_given?
250
+ yield
250
251
  find_headless_modal(options)
251
252
  else
252
253
  yield if block_given?
@@ -260,8 +261,9 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
260
261
 
261
262
  def dismiss_modal(_type, options={})
262
263
  if headless_chrome?
264
+ raise ArgumentError, "Block that triggers the system modal is missing" unless block_given?
263
265
  insert_modal_handlers(false, options[:with], options[:text])
264
- yield if block_given?
266
+ yield
265
267
  find_headless_modal(options)
266
268
  else
267
269
  yield if block_given?
@@ -209,7 +209,7 @@ private
209
209
  def set_text(value, options)
210
210
  if readonly?
211
211
  warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"
212
- elsif value.to_s.empty?
212
+ elsif value.to_s.empty? && options[:clear].nil?
213
213
  native.clear
214
214
  else
215
215
  if options[:clear] == :backspace
@@ -247,15 +247,16 @@ module Capybara
247
247
  raise_server_error!
248
248
  @touched = true
249
249
 
250
- visit_uri = URI.parse(visit_uri.to_s)
250
+ visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
251
251
 
252
252
  uri_base = if @server
253
- visit_uri.port = @server.port if config.always_include_port && (visit_uri.port == visit_uri.default_port)
254
- URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
253
+ ::Addressable::URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
255
254
  else
256
- config.app_host && URI.parse(config.app_host)
255
+ config.app_host && ::Addressable::URI.parse(config.app_host)
257
256
  end
258
257
 
258
+ uri_base.port ||= @server.port if @server && config.always_include_port
259
+
259
260
  # TODO - this is only for compatability with previous 2.x behavior that concatenated
260
261
  # Capybara.app_host and a "relative" path - Consider removing in 3.0
261
262
  # @abotalov brought up a good point about this behavior potentially being useful to people
@@ -264,7 +265,9 @@ module Capybara
264
265
  visit_uri.path = uri_base.path + visit_uri.path
265
266
  end
266
267
 
267
- visit_uri = uri_base.merge(visit_uri) unless uri_base.nil?
268
+ if uri_base && [nil, 'http', 'https'].include?(visit_uri.scheme)
269
+ visit_uri = uri_base.merge(visit_uri.to_hash.delete_if { |k,v| v.nil? })
270
+ end
268
271
 
269
272
  driver.visit(visit_uri.to_s)
270
273
  end
@@ -640,13 +643,22 @@ module Capybara
640
643
  # Execute the block, accepting a alert.
641
644
  #
642
645
  # @!macro modal_params
646
+ # Expects a block whose actions will trigger the display modal to appear
647
+ # @example
648
+ # $0 do
649
+ # click_link('link that triggers appearance of system modal')
650
+ # end
643
651
  # @overload $0(text, options = {}, &blk)
644
652
  # @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched
653
+ # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
654
+ # @yield Block whose actions will trigger the system modal
645
655
  # @overload $0(options = {}, &blk)
646
- # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
656
+ # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
657
+ # @yield Block whose actions will trigger the system modal
647
658
  # @return [String] the message shown in the modal
648
659
  # @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
649
660
  #
661
+ #
650
662
  def accept_alert(text_or_options=nil, options={}, &blk)
651
663
  accept_modal(:alert, text_or_options, options, &blk)
652
664
  end
@@ -37,6 +37,9 @@ $(function() {
37
37
  $('#with_change_event').change(function() {
38
38
  $('body').append($('<p class="change_event_triggered"></p>').text(this.value));
39
39
  });
40
+ $('#with_change_event').on('input', function() {
41
+ $('body').append($('<p class="input_event_triggered"></p>').text(this.value));
42
+ });
40
43
  $('#checkbox_with_event').click(function() {
41
44
  $('body').append('<p id="checkbox_event_triggered">Checkbox event triggered</p>');
42
45
  });
@@ -3,7 +3,7 @@ require "capybara/spec/test_app"
3
3
 
4
4
  Capybara::SpecHelper.spec '#current_url, #current_path, #current_host' do
5
5
  before :all do
6
- @servers = 2.times.map { Capybara::Server.new(TestApp.clone).boot }
6
+ @servers = 2.times.map { Capybara::Server.new(TestApp.new).boot }
7
7
  # sanity check
8
8
  expect(@servers[0].port).not_to eq(@servers[1].port)
9
9
  expect(@servers.map { |s| s.port }).not_to include 80
@@ -98,9 +98,9 @@ Capybara::SpecHelper.spec '#current_url, #current_path, #current_host' do
98
98
  end
99
99
 
100
100
  it "doesn't raise exception on a nil current_url" do
101
+ skip "Only makes sense when there is a real driver" unless @session.respond_to?(:driver)
102
+ allow(@session.driver).to receive(:current_url) { nil }
101
103
  @session.visit("/")
102
- allow_any_instance_of(Capybara::Session).to receive(:current_url) { nil }
103
-
104
104
  expect { @session.current_url }.not_to raise_exception
105
105
  expect { @session.current_path }.not_to raise_exception
106
106
  end
@@ -94,7 +94,7 @@ Capybara::SpecHelper.spec "#select" do
94
94
 
95
95
  context "with an option that doesn't exist" do
96
96
  it "should raise an error" do
97
- msg = "Unable to find visible option \"Does not Exist\""
97
+ msg = /^Unable to find visible option "Does not Exist" within/
98
98
  expect do
99
99
  @session.select('Does not Exist', from: 'form_locale')
100
100
  end.to raise_error(Capybara::ElementNotFound, msg)
@@ -64,7 +64,7 @@ Capybara::SpecHelper.spec "#unselect" do
64
64
 
65
65
  context "with an option that doesn't exist" do
66
66
  it "should raise an error" do
67
- msg = 'Unable to find visible option "Does not Exist"'
67
+ msg = /^Unable to find visible option "Does not Exist" within/
68
68
  expect do
69
69
  @session.unselect('Does not Exist', from: 'form_underwear')
70
70
  end.to raise_error(Capybara::ElementNotFound, msg)
@@ -63,6 +63,23 @@ Capybara::SpecHelper.spec '#visit' do
63
63
  expect(URI.parse(@session.current_url).port).to eq(root_uri.port)
64
64
  expect(@session).to have_content('Another World')
65
65
  end
66
+
67
+ it "should add the server port to a visited url if no port specified", requires: [:server] do
68
+ expect(@session.driver).to receive(:visit).with("http://www.example.com:#{@session.server.port}")
69
+ @session.visit("http://www.example.com")
70
+ end
71
+
72
+ it "should not override the visit specified port even if default for scheme", requires: [:server] do
73
+ expect(@session.driver).to receive(:visit).with("http://www.example.com:80")
74
+ @session.visit('http://www.example.com:80')
75
+ end
76
+
77
+ it "should give preference to app_host port if specified", requires: [:server] do
78
+ Capybara.app_host = "http://www.example.com:6666"
79
+ expect(@session.driver).to receive(:visit).with("http://www.example.com:6666/random")
80
+ @session.visit('/random')
81
+ end
82
+
66
83
  end
67
84
 
68
85
  context "without a server", requires: [:server] do
@@ -101,7 +101,7 @@ Capybara::SpecHelper.spec '#within' do
101
101
  expect do
102
102
  @session.within(".//div[@id='doesnotexist']") do
103
103
  end
104
- end.to raise_error(Capybara::ElementNotFound)
104
+ end.to raise_error(Capybara::ElementNotFound, %Q{Unable to find visible xpath ".//div[@id='doesnotexist']" within #<Capybara::Node::Element tag="div" path="/html/body/div[1]">})
105
105
  end.to_not change { @session.has_xpath?(".//div[@id='another_foo']") }.from(false)
106
106
  end
107
107
  end.to_not change { @session.has_xpath?(".//div[@id='another_foo']") }.from(true)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Capybara
3
- VERSION = '2.15.1'
3
+ VERSION = '2.15.2'
4
4
  end
@@ -35,27 +35,13 @@ RSpec.describe Capybara do
35
35
  end
36
36
 
37
37
  describe '.register_server' do
38
- before do
39
- Capybara.reuse_server = false
40
- @old_server = Capybara.server
41
- end
42
-
43
- after do
44
- Capybara.server(&@old_server)
45
- Capybara.reuse_server = true
46
- end
47
-
48
38
  it "should add a new server" do
49
- skip "JRuby fails this because of path issues to geckodriver I think. Its tested in other runs - not worth figuring out at this time" if RUBY_PLATFORM == 'java'
50
-
51
- require 'rack/handler/webrick'
39
+ handler = double("handler")
52
40
  Capybara.register_server :blob do |app, port, host|
53
- Rack::Handler::WEBrick.run(app, Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log::new(nil, 0))
41
+ handler.run
54
42
  end
55
- Capybara.server = :blob
56
- session = Capybara::Session.new(:selenium, TestApp.new)
57
- session.visit('/')
58
- expect(session.body).to include("Hello world!")
43
+
44
+ expect(Capybara.servers).to have_key(:blob)
59
45
  end
60
46
  end
61
47
 
@@ -5,9 +5,9 @@ require 'shared_selenium_session'
5
5
 
6
6
  CHROME_DRIVER = if ENV['HEADLESS'] then :selenium_chrome_headless else :selenium_chrome end
7
7
 
8
- if ENV['HEADLESS'] && ENV['TRAVIS']
9
- Selenium::WebDriver::Chrome.path='/usr/bin/google-chrome-beta'
10
- end
8
+ # if ENV['HEADLESS'] && ENV['TRAVIS']
9
+ # Selenium::WebDriver::Chrome.path='/usr/bin/google-chrome-beta'
10
+ # end
11
11
 
12
12
  Capybara.register_driver :selenium_chrome_clear_storage do |app|
13
13
  chrome_options = {
@@ -4,13 +4,20 @@ require "selenium-webdriver"
4
4
  require 'shared_selenium_session'
5
5
  require 'rspec/shared_spec_matchers'
6
6
 
7
- ENV['MOZ_HEADLESS']='1' if ENV['HEADLESS']
7
+ browser_options = ::Selenium::WebDriver::Firefox::Options.new()
8
+ browser_options.args << '--headless' if ENV['HEADLESS']
9
+ browser_options.add_preference 'dom.file.createInChild', true
10
+ # browser_options.add_option("log", {"level": "trace"})
8
11
 
9
12
  Capybara.register_driver :selenium_marionette do |app|
13
+ # ::Selenium::WebDriver.logger.level = "debug"
10
14
  Capybara::Selenium::Driver.new(
11
15
  app,
12
16
  browser: :firefox,
13
- desired_capabilities: {marionette: true}
17
+ desired_capabilities: {:marionette => true},
18
+ options: browser_options
19
+ # Get a trace level log from geckodriver
20
+ # :driver_opts => { args: ['-vv'] }
14
21
  )
15
22
  end
16
23
 
@@ -20,10 +27,13 @@ Capybara.register_driver :selenium_marionette_clear_storage do |app|
20
27
  browser: :firefox,
21
28
  desired_capabilities: {marionette: true},
22
29
  clear_local_storage: true,
23
- clear_session_storage: true
30
+ clear_session_storage: true,
31
+ options: browser_options
24
32
  )
25
33
  end
26
34
 
35
+
36
+
27
37
  module TestSessions
28
38
  SeleniumMarionette = Capybara::Session.new(:selenium_marionette, TestApp)
29
39
  end
@@ -57,12 +57,28 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
57
57
 
58
58
  describe "#accept_alert" do
59
59
  it "supports a blockless mode" do
60
- skip "Headless Chrome doesn't support blockless modal methods" if @session.driver.send(:headless_chrome?)
61
60
  @session.visit('/with_js')
61
+ skip "Headless Chrome doesn't support blockless modal methods" if @session.driver.send(:headless_chrome?)
62
62
  @session.click_link('Open alert')
63
63
  @session.accept_alert
64
64
  expect{@session.driver.browser.switch_to.alert}.to raise_error(Selenium::WebDriver::Error::NoAlertPresentError)
65
65
  end
66
+
67
+ it "raises if block is missing" do
68
+ @session.visit('/with_js')
69
+ skip "Only Headless Chrome requires the block due to system modal JS injection" unless @session.driver.send(:headless_chrome?)
70
+ expect { @session.accept_alert }.to raise_error(ArgumentError)
71
+ end
72
+ end
73
+
74
+ context '#fill_in_with empty string and no options' do
75
+ it 'should trigger change when clearing a field' do
76
+ @session.visit('/with_js')
77
+ @session.fill_in('with_change_event', with: '')
78
+ # click outside the field to trigger the change event
79
+ @session.find(:css, 'body').click
80
+ expect(@session).to have_selector(:css, '.change_event_triggered', match: :one)
81
+ end
66
82
  end
67
83
 
68
84
  context "#fill_in with { :clear => :backspace } fill_option", requires: [:js] do
@@ -90,6 +106,15 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
90
106
  @session.find(:css, 'body').click
91
107
  expect(@session).to have_selector(:css, '.change_event_triggered', match: :one)
92
108
  end
109
+
110
+ it 'should trigger input event field_value.length times' do
111
+ @session.visit('/with_js')
112
+ @session.fill_in('with_change_event', with: '',
113
+ fill_options: { :clear => :backspace })
114
+ # click outside the field to trigger the change event
115
+ @session.find(:css, 'body').click
116
+ expect(@session).to have_xpath('//p[@class="input_event_triggered"]', count: 13)
117
+ end
93
118
  end
94
119
 
95
120
  context "#fill_in with { clear: :none } fill_options" do
@@ -161,7 +186,8 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
161
186
  it "outputs obsolete elements" do
162
187
  @session.visit('/form')
163
188
  el = @session.find(:button, 'Click me!').click
164
- sleep 2
189
+ expect(@session).to have_no_button('Click me!')
190
+ expect(el).not_to receive(:synchronize)
165
191
  expect(el.inspect).to eq "Obsolete #<Capybara::Node::Element>"
166
192
  end
167
193
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.15.1
4
+ version: 2.15.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Walpole
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain:
12
12
  - gem-public_cert.pem
13
- date: 2017-08-04 00:00:00.000000000 Z
13
+ date: 2017-10-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: nokogiri
@@ -506,7 +506,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
506
506
  version: '0'
507
507
  requirements: []
508
508
  rubyforge_project:
509
- rubygems_version: 2.6.11
509
+ rubygems_version: 2.6.13
510
510
  signing_key:
511
511
  specification_version: 4
512
512
  summary: Capybara aims to simplify the process of integration testing Rack applications,