capybara 2.13.0 → 2.14.0

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