capybara 2.15.1 → 2.15.2

Sign up to get free protection for your applications and to get access to all the features.
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,