capybara 2.13.0 → 2.14.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +82 -17
  3. data/README.md +29 -0
  4. data/lib/capybara.rb +81 -116
  5. data/lib/capybara/config.rb +121 -0
  6. data/lib/capybara/cucumber.rb +1 -0
  7. data/lib/capybara/driver/base.rb +6 -0
  8. data/lib/capybara/dsl.rb +1 -3
  9. data/lib/capybara/helpers.rb +3 -3
  10. data/lib/capybara/node/actions.rb +2 -2
  11. data/lib/capybara/node/base.rb +7 -2
  12. data/lib/capybara/node/element.rb +7 -1
  13. data/lib/capybara/node/finders.rb +13 -3
  14. data/lib/capybara/node/matchers.rb +15 -4
  15. data/lib/capybara/node/simple.rb +5 -0
  16. data/lib/capybara/queries/base_query.rb +8 -3
  17. data/lib/capybara/queries/selector_query.rb +11 -9
  18. data/lib/capybara/queries/text_query.rb +9 -4
  19. data/lib/capybara/rack_test/browser.rb +8 -5
  20. data/lib/capybara/rspec.rb +3 -1
  21. data/lib/capybara/rspec/matcher_proxies.rb +41 -0
  22. data/lib/capybara/rspec/matchers.rb +19 -5
  23. data/lib/capybara/selector.rb +13 -4
  24. data/lib/capybara/selector/selector.rb +3 -3
  25. data/lib/capybara/selenium/driver.rb +20 -6
  26. data/lib/capybara/selenium/node.rb +6 -2
  27. data/lib/capybara/server.rb +6 -5
  28. data/lib/capybara/session.rb +71 -14
  29. data/lib/capybara/session/config.rb +100 -0
  30. data/lib/capybara/spec/public/test.js +1 -1
  31. data/lib/capybara/spec/session/all_spec.rb +11 -0
  32. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +24 -8
  33. data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
  34. data/lib/capybara/spec/session/find_field_spec.rb +1 -0
  35. data/lib/capybara/spec/session/find_spec.rb +4 -3
  36. data/lib/capybara/spec/session/has_selector_spec.rb +1 -3
  37. data/lib/capybara/spec/session/node_spec.rb +23 -17
  38. data/lib/capybara/spec/session/reset_session_spec.rb +1 -1
  39. data/lib/capybara/spec/session/window/become_closed_spec.rb +4 -4
  40. data/lib/capybara/spec/spec_helper.rb +22 -0
  41. data/lib/capybara/spec/views/form.erb +6 -1
  42. data/lib/capybara/spec/views/with_html.erb +1 -0
  43. data/lib/capybara/version.rb +1 -1
  44. data/lib/capybara/window.rb +1 -1
  45. data/spec/capybara_spec.rb +14 -2
  46. data/spec/dsl_spec.rb +1 -0
  47. data/spec/per_session_config_spec.rb +67 -0
  48. data/spec/rspec/shared_spec_matchers.rb +2 -2
  49. data/spec/rspec/views_spec.rb +4 -0
  50. data/spec/rspec_spec.rb +77 -0
  51. data/spec/session_spec.rb +44 -0
  52. data/spec/shared_selenium_session.rb +9 -0
  53. data/spec/spec_helper.rb +4 -0
  54. metadata +7 -3
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+ require 'forwardable'
3
+ require 'capybara/session/config'
4
+
5
+ module Capybara
6
+ class Config
7
+ extend Forwardable
8
+
9
+ OPTIONS = [:app, :reuse_server, :threadsafe, :default_wait_time, :server, :default_driver, :javascript_driver]
10
+
11
+ attr_accessor :app
12
+ attr_reader :reuse_server, :threadsafe
13
+ attr_reader :session_options
14
+ attr_writer :default_driver, :javascript_driver
15
+
16
+ SessionConfig::OPTIONS.each do |method|
17
+ def_delegators :session_options, method, "#{method}="
18
+ end
19
+
20
+ def initialize
21
+ @session_options = Capybara::SessionConfig.new
22
+ end
23
+
24
+ def reuse_server=(bool)
25
+ @reuse_server = bool
26
+ end
27
+
28
+ def threadsafe=(bool)
29
+ warn "Capybara.threadsafe == true is a BETA feature and may change in future minor versions" if bool
30
+ raise "Threadsafe setting cannot be changed once a session is created" if (bool != threadsafe) && Session.instance_created?
31
+ @threadsafe = bool
32
+ end
33
+
34
+ ##
35
+ #
36
+ # Return the proc that Capybara will call to run the Rack application.
37
+ # The block returned receives a rack app, port, and host/ip and should run a Rack handler
38
+ # By default, Capybara will try to run webrick.
39
+ #
40
+ def server(&block)
41
+ if block_given?
42
+ warn "DEPRECATED: Passing a block to Capybara::server is deprecated, please use Capybara::register_server instead"
43
+ @server = block
44
+ else
45
+ @server
46
+ end
47
+ end
48
+
49
+ ##
50
+ #
51
+ # Set the server to use.
52
+ #
53
+ # Capybara.server = :webrick
54
+ #
55
+ # @param [Symbol] name Name of the server type to use
56
+ # @see register_server
57
+ #
58
+ def server=(name)
59
+ @server = if name.respond_to? :call
60
+ name
61
+ else
62
+ Capybara.servers[name.to_sym]
63
+ end
64
+ end
65
+
66
+ ##
67
+ #
68
+ # @return [Symbol] The name of the driver to use by default
69
+ #
70
+ def default_driver
71
+ @default_driver || :rack_test
72
+ end
73
+
74
+ ##
75
+ #
76
+ # @return [Symbol] The name of the driver used when JavaScript is needed
77
+ #
78
+ def javascript_driver
79
+ @javascript_driver || :selenium
80
+ end
81
+
82
+ # @deprecated Use default_max_wait_time instead
83
+ def default_wait_time
84
+ deprecate('default_wait_time', 'default_max_wait_time', true)
85
+ default_max_wait_time
86
+ end
87
+
88
+ # @deprecated Use default_max_wait_time= instead
89
+ def default_wait_time=(t)
90
+ deprecate('default_wait_time=', 'default_max_wait_time=')
91
+ self.default_max_wait_time = t
92
+ end
93
+
94
+ def deprecate(method, alternate_method, once=false)
95
+ @deprecation_notified ||= {}
96
+ warn "DEPRECATED: ##{method} is deprecated, please use ##{alternate_method} instead" unless once and @deprecation_notified[method]
97
+ @deprecation_notified[method]=true
98
+ end
99
+ end
100
+
101
+ class ConfigureDeprecator
102
+ def initialize(config)
103
+ @config = config
104
+ end
105
+
106
+ def method_missing(m, *args, &block)
107
+ if @config.respond_to?(m)
108
+ @config.public_send(m, *args, &block)
109
+ elsif Capybara.respond_to?(m)
110
+ warn "Calling #{m} from Capybara.configure is deprecated - please call it on Capybara directly ( Capybara.#{m}(...) )"
111
+ Capybara.public_send(m, *args, &block)
112
+ else
113
+ super
114
+ end
115
+ end
116
+
117
+ def respond_to_missing?(m, include_private = false)
118
+ @config.respond_to_missing?(m, include_private) || Capybara.respond_to_missing?(m, include_private)
119
+ end
120
+ end
121
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'capybara/dsl'
3
3
  require 'capybara/rspec/matchers'
4
+ require 'capybara/rspec/matcher_proxies'
4
5
 
5
6
  World(Capybara::DSL)
6
7
  World(Capybara::RSpecMatchers)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  class Capybara::Driver::Base
3
+ attr_writer :session
4
+
3
5
  def current_url
4
6
  raise NotImplementedError
5
7
  end
@@ -139,6 +141,10 @@ class Capybara::Driver::Base
139
141
  false
140
142
  end
141
143
 
144
+ def session_options
145
+ (@session && @session.config) || Capybara.session_options
146
+ end
147
+
142
148
  # @deprecated This method is being removed
143
149
  def browser_initialized?
144
150
  warn "DEPRECATED: #browser_initialized? is deprecated and will be removed in the next version of Capybara"
@@ -21,12 +21,10 @@ module Capybara
21
21
  Capybara.using_session(name, &block)
22
22
  end
23
23
 
24
- ##
25
- #
26
24
  # Shortcut to using a different wait time.
27
25
  #
28
26
  def using_wait_time(seconds, &block)
29
- Capybara.using_wait_time(seconds, &block)
27
+ page.using_wait_time(seconds, &block)
30
28
  end
31
29
 
32
30
  ##
@@ -46,11 +46,11 @@ module Capybara
46
46
  # @param [String] html HTML code to inject into
47
47
  # @return [String] The modified HTML code
48
48
  #
49
- def inject_asset_host(html)
50
- if Capybara.asset_host && Nokogiri::HTML(html).css("base").empty?
49
+ def inject_asset_host(html, asset_host = Capybara.asset_host)
50
+ if asset_host && Nokogiri::HTML(html).css("base").empty?
51
51
  match = html.match(/<head[^<]*?>/)
52
52
  if match
53
- return html.clone.insert match.end(0), "<base href='#{Capybara.asset_host}' />"
53
+ return html.clone.insert match.end(0), "<base href='#{asset_host}' />"
54
54
  end
55
55
  end
56
56
 
@@ -293,9 +293,9 @@ module Capybara
293
293
 
294
294
  def _check_with_label(selector, checked, locator, options)
295
295
  locator, options = nil, locator if locator.is_a? Hash
296
- allow_label_click = options.delete(:allow_label_click) { Capybara.automatic_label_click }
296
+ allow_label_click = options.delete(:allow_label_click) { session_options.automatic_label_click }
297
297
 
298
- synchronize(Capybara::Queries::BaseQuery::wait(options)) do
298
+ synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
299
299
  begin
300
300
  el = find(selector, locator, options)
301
301
  el.set(checked)
@@ -74,7 +74,7 @@ module Capybara
74
74
  # @return [Object] The result of the given block
75
75
  # @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
76
76
  #
77
- def synchronize(seconds=Capybara.default_max_wait_time, options = {})
77
+ def synchronize(seconds=session_options.default_max_wait_time, options = {})
78
78
  start_time = Capybara::Helpers.monotonic_time
79
79
 
80
80
  if session.synchronized
@@ -90,7 +90,7 @@ module Capybara
90
90
  raise e if (Capybara::Helpers.monotonic_time - start_time) >= seconds
91
91
  sleep(0.05)
92
92
  raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Capybara::Helpers.monotonic_time == start_time
93
- reload if Capybara.automatic_reload
93
+ reload if session_options.automatic_reload
94
94
  retry
95
95
  ensure
96
96
  session.synchronized = false
@@ -114,6 +114,11 @@ module Capybara
114
114
  query_scope
115
115
  end
116
116
 
117
+ # @api private
118
+ def session_options
119
+ session.config
120
+ end
121
+
117
122
  protected
118
123
 
119
124
  def catch_error?(error, errors = nil)
@@ -55,7 +55,7 @@ module Capybara
55
55
  # @return [String] The text of the element
56
56
  #
57
57
  def text(type=nil)
58
- type ||= :all unless Capybara.ignore_hidden_elements or Capybara.visible_text_only
58
+ type ||= :all unless session_options.ignore_hidden_elements or session_options.visible_text_only
59
59
  synchronize do
60
60
  if type == :all
61
61
  base.all_text
@@ -374,6 +374,12 @@ module Capybara
374
374
  %(#<Capybara::Node::Element tag="#{tag_name}" path="#{path}">)
375
375
  rescue NotSupportedByDriverError
376
376
  %(#<Capybara::Node::Element tag="#{tag_name}">)
377
+ rescue => e
378
+ if session.driver.invalid_element_errors.any? { |et| e.is_a?(et)}
379
+ %(Obsolete #<Capybara::Node::Element>)
380
+ else
381
+ raise
382
+ end
377
383
  end
378
384
  end
379
385
  end
@@ -29,11 +29,16 @@ module Capybara
29
29
  # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
30
30
  #
31
31
  def find(*args, &optional_filter_block)
32
+ if args.last.is_a? Hash
33
+ args.last[:session_options] = session_options
34
+ else
35
+ args.push(session_options: session_options)
36
+ end
32
37
  query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
33
38
  synchronize(query.wait) do
34
- if (query.match == :smart or query.match == :prefer_exact) and query.supports_exact?
39
+ if (query.match == :smart or query.match == :prefer_exact)
35
40
  result = query.resolve_for(self, true)
36
- result = query.resolve_for(self, false) if result.empty? && !query.exact?
41
+ result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
37
42
  else
38
43
  result = query.resolve_for(self)
39
44
  end
@@ -208,6 +213,11 @@ module Capybara
208
213
  # @return [Capybara::Result] A collection of found elements
209
214
  #
210
215
  def all(*args, &optional_filter_block)
216
+ if args.last.is_a? Hash
217
+ args.last[:session_options] = session_options
218
+ else
219
+ args.push(session_options: session_options)
220
+ end
211
221
  query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
212
222
  synchronize(query.wait) do
213
223
  result = query.resolve_for(self)
@@ -233,7 +243,7 @@ module Capybara
233
243
  # @return [Capybara::Node::Element] The found element or nil
234
244
  #
235
245
  def first(*args, &optional_filter_block)
236
- if Capybara.wait_on_first_by_default
246
+ if session_options.wait_on_first_by_default
237
247
  options = if args.last.is_a?(Hash) then args.pop.dup else {} end
238
248
  args.push({minimum: 1}.merge(options))
239
249
  end
@@ -114,8 +114,8 @@ module Capybara
114
114
  #
115
115
  def assert_all_of_selectors(*args, &optional_filter_block)
116
116
  options = if args.last.is_a?(Hash) then args.pop.dup else {} end
117
- selector = if args.first.is_a?(Symbol) then args.shift else Capybara.default_selector end
118
- wait = options.fetch(:wait, Capybara.default_max_wait_time)
117
+ selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
118
+ wait = options.fetch(:wait, session_options.default_max_wait_time)
119
119
  synchronize(wait) do
120
120
  args.each do |locator|
121
121
  assert_selector(selector, locator, options, &optional_filter_block)
@@ -140,8 +140,8 @@ module Capybara
140
140
  #
141
141
  def assert_none_of_selectors(*args, &optional_filter_block)
142
142
  options = if args.last.is_a?(Hash) then args.pop.dup else {} end
143
- selector = if args.first.is_a?(Symbol) then args.shift else Capybara.default_selector end
144
- wait = options.fetch(:wait, Capybara.default_max_wait_time)
143
+ selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
144
+ wait = options.fetch(:wait, session_options.default_max_wait_time)
145
145
  synchronize(wait) do
146
146
  args.each do |locator|
147
147
  assert_no_selector(selector, locator, options, &optional_filter_block)
@@ -680,6 +680,7 @@ module Capybara
680
680
  private
681
681
 
682
682
  def _verify_selector_result(query_args, optional_filter_block, &result_block)
683
+ _set_query_session_options(query_args)
683
684
  query = Capybara::Queries::SelectorQuery.new(*query_args, &optional_filter_block)
684
685
  synchronize(query.wait) do
685
686
  result = query.resolve_for(self)
@@ -689,6 +690,7 @@ module Capybara
689
690
  end
690
691
 
691
692
  def _verify_match_result(query_args, optional_filter_block, &result_block)
693
+ _set_query_session_options(query_args)
692
694
  query = Capybara::Queries::MatchQuery.new(*query_args, &optional_filter_block)
693
695
  synchronize(query.wait) do
694
696
  result = query.resolve_for(self.query_scope)
@@ -698,6 +700,7 @@ module Capybara
698
700
  end
699
701
 
700
702
  def _verify_text(query_args)
703
+ _set_query_session_options(query_args)
701
704
  query = Capybara::Queries::TextQuery.new(*query_args)
702
705
  synchronize(query.wait) do
703
706
  count = query.resolve_for(self)
@@ -706,6 +709,14 @@ module Capybara
706
709
  return true
707
710
  end
708
711
 
712
+ def _set_query_session_options(query_args)
713
+ if query_args.last.is_a? Hash
714
+ query_args.last[:session_options] = session_options
715
+ else
716
+ query_args.push(session_options: session_options)
717
+ end
718
+ query_args
719
+ end
709
720
  end
710
721
  end
711
722
  end
@@ -173,6 +173,11 @@ module Capybara
173
173
  def find_xpath(xpath)
174
174
  native.xpath(xpath)
175
175
  end
176
+
177
+ # @api private
178
+ def session_options
179
+ Capybara.session_options
180
+ end
176
181
  end
177
182
  end
178
183
  end
@@ -6,13 +6,18 @@ module Capybara
6
6
  COUNT_KEYS = [:count, :minimum, :maximum, :between]
7
7
 
8
8
  attr_reader :options
9
+ attr_writer :session_options
10
+
11
+ def session_options
12
+ @session_options || Capybara.session_options
13
+ end
9
14
 
10
15
  def wait
11
- self.class.wait(options)
16
+ self.class.wait(options, session_options.default_max_wait_time)
12
17
  end
13
18
 
14
- def self.wait(options)
15
- options.fetch(:wait, Capybara.default_max_wait_time) || 0
19
+ def self.wait(options, default=Capybara.default_max_wait_time)
20
+ options.fetch(:wait, default) || 0
16
21
  end
17
22
 
18
23
  ##
@@ -9,11 +9,13 @@ module Capybara
9
9
 
10
10
  def initialize(*args, &filter_block)
11
11
  @options = if args.last.is_a?(Hash) then args.pop.dup else {} end
12
+ self.session_options = @options.delete(:session_options)
13
+
12
14
  @filter_block = filter_block
13
15
 
14
16
  if args[0].is_a?(Symbol)
15
17
  @selector = Selector.all.fetch(args.shift) do |selector_type|
16
- warn "Unknown selector type (:#{selector_type}), defaulting to :#{Capybara.default_selector} - This will raise an exception in a future version of Capybara"
18
+ raise ArgumentError, "Unknown selector type (:#{selector_type})"
17
19
  nil
18
20
  end
19
21
  @locator = args.shift
@@ -21,16 +23,16 @@ module Capybara
21
23
  @selector = Selector.all.values.find { |s| s.match?(args[0]) }
22
24
  @locator = args.shift
23
25
  end
24
- @selector ||= Selector.all[Capybara.default_selector]
26
+ @selector ||= Selector.all[session_options.default_selector]
25
27
 
26
28
  warn "Unused parameters passed to #{self.class.name} : #{args.to_s}" unless args.empty?
27
29
 
28
30
  # for compatibility with Capybara 2.0
29
- if Capybara.exact_options and @selector == Selector.all[:option]
31
+ if session_options.exact_options and @selector == Selector.all[:option]
30
32
  @options[:exact] = true
31
33
  end
32
34
 
33
- @expression = @selector.call(@locator, @options)
35
+ @expression = @selector.call(@locator, @options.merge(enable_aria_label: session_options.enable_aria_label))
34
36
 
35
37
  warn_exact_usage
36
38
 
@@ -89,12 +91,12 @@ module Capybara
89
91
  end
90
92
  end
91
93
 
92
- res &&= Capybara.using_wait_time(0){ @filter_block.call(node)} unless @filter_block.nil?
94
+ res &&= node.session.using_wait_time(0){ @filter_block.call(node)} unless @filter_block.nil?
93
95
  res
94
96
  end
95
97
 
96
98
  def visible
97
- case (vis = options.fetch(:visible){ @selector.default_visibility })
99
+ case (vis = options.fetch(:visible){ @selector.default_visibility(session_options.ignore_hidden_elements) })
98
100
  when true then :visible
99
101
  when false then :all
100
102
  else vis
@@ -103,11 +105,11 @@ module Capybara
103
105
 
104
106
  def exact?
105
107
  return false if !supports_exact?
106
- options.fetch(:exact, Capybara.exact)
108
+ options.fetch(:exact, session_options.exact)
107
109
  end
108
110
 
109
111
  def match
110
- options.fetch(:match, Capybara.match)
112
+ options.fetch(:match, session_options.match)
111
113
  end
112
114
 
113
115
  def xpath(exact=nil)
@@ -205,7 +207,7 @@ module Capybara
205
207
  end
206
208
 
207
209
  def exact_text
208
- options.fetch(:exact_text, Capybara.exact_text)
210
+ options.fetch(:exact_text, session_options.exact_text)
209
211
  end
210
212
  end
211
213
  end