capybara 3.1.1 → 3.2.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +19 -0
  3. data/README.md +1 -1
  4. data/lib/capybara.rb +2 -0
  5. data/lib/capybara/config.rb +2 -1
  6. data/lib/capybara/driver/base.rb +1 -1
  7. data/lib/capybara/driver/node.rb +3 -3
  8. data/lib/capybara/node/actions.rb +90 -92
  9. data/lib/capybara/node/base.rb +2 -2
  10. data/lib/capybara/node/document_matchers.rb +5 -5
  11. data/lib/capybara/node/element.rb +47 -16
  12. data/lib/capybara/node/finders.rb +13 -13
  13. data/lib/capybara/node/matchers.rb +18 -17
  14. data/lib/capybara/node/simple.rb +6 -2
  15. data/lib/capybara/queries/ancestor_query.rb +1 -1
  16. data/lib/capybara/queries/base_query.rb +3 -3
  17. data/lib/capybara/queries/current_path_query.rb +1 -1
  18. data/lib/capybara/queries/match_query.rb +8 -0
  19. data/lib/capybara/queries/selector_query.rb +97 -42
  20. data/lib/capybara/queries/sibling_query.rb +1 -1
  21. data/lib/capybara/queries/text_query.rb +12 -7
  22. data/lib/capybara/rack_test/browser.rb +9 -7
  23. data/lib/capybara/rack_test/form.rb +15 -17
  24. data/lib/capybara/rack_test/node.rb +12 -12
  25. data/lib/capybara/result.rb +26 -15
  26. data/lib/capybara/rspec.rb +1 -2
  27. data/lib/capybara/rspec/compound.rb +4 -4
  28. data/lib/capybara/rspec/matchers.rb +2 -2
  29. data/lib/capybara/selector.rb +75 -225
  30. data/lib/capybara/selector/css.rb +2 -2
  31. data/lib/capybara/selector/filter_set.rb +17 -21
  32. data/lib/capybara/selector/filters/base.rb +24 -1
  33. data/lib/capybara/selector/filters/expression_filter.rb +3 -5
  34. data/lib/capybara/selector/filters/node_filter.rb +4 -4
  35. data/lib/capybara/selector/selector.rb +221 -69
  36. data/lib/capybara/selenium/driver.rb +15 -88
  37. data/lib/capybara/selenium/node.rb +25 -28
  38. data/lib/capybara/server.rb +10 -54
  39. data/lib/capybara/server/animation_disabler.rb +43 -0
  40. data/lib/capybara/server/middleware.rb +55 -0
  41. data/lib/capybara/session.rb +29 -30
  42. data/lib/capybara/session/config.rb +11 -1
  43. data/lib/capybara/session/matchers.rb +5 -5
  44. data/lib/capybara/spec/session/assert_text_spec.rb +1 -1
  45. data/lib/capybara/spec/session/body_spec.rb +10 -12
  46. data/lib/capybara/spec/session/click_link_spec.rb +3 -3
  47. data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +1 -1
  48. data/lib/capybara/spec/session/fill_in_spec.rb +9 -0
  49. data/lib/capybara/spec/session/find_field_spec.rb +1 -1
  50. data/lib/capybara/spec/session/find_spec.rb +8 -3
  51. data/lib/capybara/spec/session/has_link_spec.rb +2 -2
  52. data/lib/capybara/spec/session/node_spec.rb +50 -0
  53. data/lib/capybara/spec/session/node_wrapper_spec.rb +5 -5
  54. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +1 -1
  55. data/lib/capybara/spec/session/window/windows_spec.rb +3 -5
  56. data/lib/capybara/spec/spec_helper.rb +4 -2
  57. data/lib/capybara/spec/views/with_animation.erb +46 -0
  58. data/lib/capybara/version.rb +1 -1
  59. data/lib/capybara/window.rb +3 -2
  60. data/spec/filter_set_spec.rb +19 -2
  61. data/spec/result_spec.rb +33 -1
  62. data/spec/rspec/features_spec.rb +6 -10
  63. data/spec/rspec/shared_spec_matchers.rb +4 -4
  64. data/spec/selector_spec.rb +74 -4
  65. data/spec/selenium_spec_marionette.rb +2 -0
  66. data/spec/server_spec.rb +1 -1
  67. data/spec/session_spec.rb +12 -0
  68. data/spec/shared_selenium_session.rb +30 -0
  69. metadata +8 -9
  70. data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
  71. data/.yard/templates_custom/default/class/html/setup.rb +0 -17
  72. data/.yard/yard_extensions.rb +0 -78
  73. data/.yardopts +0 -1
@@ -26,7 +26,7 @@ module Capybara
26
26
  # end
27
27
  #
28
28
  # Session provides a number of methods for controlling the navigation of the page, such as +visit+,
29
- # +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
29
+ # +current_path, and so on. It also delegates a number of methods to a Capybara::Document, representing
30
30
  # the current HTML document. This allows interaction:
31
31
  #
32
32
  # session.fill_in('q', with: 'Capybara')
@@ -83,10 +83,10 @@ module Capybara
83
83
  raise "A configuration block is only accepted when Capybara.threadsafe == true" unless Capybara.threadsafe
84
84
  yield config
85
85
  end
86
- @server = if config.run_server and @app and driver.needs_server?
87
- Capybara::Server.new(@app, port: config.server_port, host: config.server_host, reportable_errors: config.server_errors).boot
88
- else
89
- nil
86
+ @server = if config.run_server && @app && driver.needs_server?
87
+ server_options = { port: config.server_port, host: config.server_host, reportable_errors: config.server_errors }
88
+ server_options[:extra_middleware] = [Capybara::Server::AnimationDisabler] if config.disable_animation
89
+ Capybara::Server.new(@app, server_options).boot
90
90
  end
91
91
  @touched = false
92
92
  end
@@ -126,7 +126,7 @@ module Capybara
126
126
  driver.reset!
127
127
  @touched = false
128
128
  end
129
- @server.wait_for_pending_requests if @server
129
+ @server&.wait_for_pending_requests
130
130
  raise_server_error!
131
131
  end
132
132
  alias_method :cleanup!, :reset!
@@ -189,14 +189,11 @@ module Capybara
189
189
  # Addressable parsing is more lenient than URI
190
190
  uri = ::Addressable::URI.parse(current_url)
191
191
 
192
- # If current_url ends up being nil, won't be able to call .path on a NilClass.
193
- return nil if uri.nil?
194
-
195
192
  # Addressable doesn't support opaque URIs - we want nil here
196
- return nil if uri.scheme == "about"
193
+ return nil if uri&.scheme == "about"
197
194
 
198
- path = uri.path
199
- path if path && !path.empty?
195
+ path = uri&.path
196
+ path unless path&.empty?
200
197
  end
201
198
 
202
199
  ##
@@ -248,11 +245,10 @@ module Capybara
248
245
 
249
246
  visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
250
247
 
251
- uri_base = if @server
252
- ::Addressable::URI.parse(config.app_host || "http#{'s' if @server.using_ssl?}://#{@server.host}:#{@server.port}")
253
- else
254
- config.app_host && ::Addressable::URI.parse(config.app_host)
255
- end
248
+ base = config.app_host
249
+ base ||= "http#{'s' if @server.using_ssl?}://#{@server.host}:#{@server.port}" if @server
250
+
251
+ uri_base = ::Addressable::URI.parse(base)
256
252
 
257
253
  if uri_base && [nil, 'http', 'https'].include?(visit_uri.scheme)
258
254
  if visit_uri.relative?
@@ -337,7 +333,7 @@ module Capybara
337
333
  new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args)
338
334
  begin
339
335
  scopes.push(new_scope)
340
- yield
336
+ yield if block_given?
341
337
  ensure
342
338
  scopes.pop
343
339
  end
@@ -411,7 +407,7 @@ module Capybara
411
407
  #
412
408
  # @overload within_frame(element)
413
409
  # @param [Capybara::Node::Element] frame element
414
- # @overload within_frame([kind = :frame], locator, options = {})
410
+ # @overload within_frame([kind = :frame], locator, **options)
415
411
  # @param [Symbol] kind Optional selector type (:css, :xpath, :field, etc.) - Defaults to :frame
416
412
  # @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
417
413
  # @overload within_frame(index)
@@ -419,7 +415,7 @@ module Capybara
419
415
  def within_frame(*args)
420
416
  switch_to_frame(_find_frame(*args))
421
417
  begin
422
- yield
418
+ yield if block_given?
423
419
  ensure
424
420
  switch_to_frame(:parent)
425
421
  end
@@ -475,9 +471,8 @@ module Capybara
475
471
  # @raise [ArgumentError] if both or neither arguments were provided
476
472
  #
477
473
  def switch_to_window(window = nil, **options, &window_locator)
478
- block_given = block_given?
479
- raise ArgumentError, "`switch_to_window` can take either a block or a window, not both" if window && block_given
480
- raise ArgumentError, "`switch_to_window`: either window or block should be provided" if !window && !block_given
474
+ raise ArgumentError, "`switch_to_window` can take either a block or a window, not both" if window && block_given?
475
+ raise ArgumentError, "`switch_to_window`: either window or block should be provided" if !window && !block_given?
481
476
  unless scopes.last.nil?
482
477
  raise Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from "\
483
478
  "`within` or `within_frame` blocks."
@@ -521,7 +516,7 @@ module Capybara
521
516
  end
522
517
 
523
518
  begin
524
- yield
519
+ yield if block_given?
525
520
  ensure
526
521
  _switch_to_window(original) unless original == window_or_proc
527
522
  end
@@ -569,7 +564,7 @@ module Capybara
569
564
  #
570
565
  def execute_script(script, *args)
571
566
  @touched = true
572
- driver.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg })
567
+ driver.execute_script(script, *driver_args(args))
573
568
  end
574
569
 
575
570
  ##
@@ -583,7 +578,7 @@ module Capybara
583
578
  #
584
579
  def evaluate_script(script, *args)
585
580
  @touched = true
586
- result = driver.evaluate_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg })
581
+ result = driver.evaluate_script(script, *driver_args(args))
587
582
  element_script_result(result)
588
583
  end
589
584
 
@@ -596,7 +591,7 @@ module Capybara
596
591
  #
597
592
  def evaluate_async_script(script, *args)
598
593
  @touched = true
599
- result = driver.evaluate_async_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg })
594
+ result = driver.evaluate_async_script(script, *driver_args(args))
600
595
  element_script_result(result)
601
596
  end
602
597
 
@@ -610,11 +605,11 @@ module Capybara
610
605
  # $0 do
611
606
  # click_link('link that triggers appearance of system modal')
612
607
  # end
613
- # @overload $0(text, options = {}, &blk)
608
+ # @overload $0(text, **options, &blk)
614
609
  # @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched
615
610
  # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
616
611
  # @yield Block whose actions will trigger the system modal
617
- # @overload $0(options = {}, &blk)
612
+ # @overload $0(**options, &blk)
618
613
  # @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time to wait for the modal to appear after executing the block.
619
614
  # @yield Block whose actions will trigger the system modal
620
615
  # @return [String] the message shown in the modal
@@ -799,6 +794,10 @@ module Capybara
799
794
 
800
795
  @@instance_created = false
801
796
 
797
+ def driver_args(args)
798
+ args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg }
799
+ end
800
+
802
801
  def accept_modal(type, text_or_options, options, &blk)
803
802
  driver.accept_modal(type, modal_options(text_or_options, options), &blk)
804
803
  end
@@ -878,7 +877,7 @@ module Capybara
878
877
  driver.switch_to_window handle
879
878
  return Window.new(self, handle) if yield
880
879
  end
881
- rescue => e
880
+ rescue StandardError => e
882
881
  driver.switch_to_window(original_window_handle)
883
882
  raise e
884
883
  else
@@ -7,7 +7,7 @@ module Capybara
7
7
  OPTIONS = %i[always_include_port run_server default_selector default_max_wait_time ignore_hidden_elements
8
8
  automatic_reload match exact exact_text raise_server_errors visible_text_only
9
9
  automatic_label_click enable_aria_label save_path asset_host default_host app_host
10
- server_host server_port server_errors].freeze
10
+ server_host server_port server_errors default_set_options disable_animation].freeze
11
11
 
12
12
  attr_accessor(*OPTIONS)
13
13
 
@@ -50,6 +50,10 @@ module Capybara
50
50
  # See {Capybara.configure}
51
51
  # @!method server_errors
52
52
  # See {Capybara.configure}
53
+ # @!method default_set_options
54
+ # See {Capybara.configure}
55
+ # @!method disable_animation
56
+ # See {Capybara.configure}
53
57
 
54
58
  remove_method :server_host
55
59
 
@@ -78,6 +82,12 @@ module Capybara
78
82
  @default_host = url
79
83
  end
80
84
 
85
+ remove_method :disable_animation=
86
+ def disable_animation=(bool)
87
+ warn "Capybara.disable_animation is a beta feature - it may change/disappear in a future point version" if bool
88
+ @disable_animation = bool
89
+ end
90
+
81
91
  def initialize_copy(other)
82
92
  super
83
93
  @server_errors = @server_errors.dup
@@ -9,9 +9,9 @@ module Capybara
9
9
  # the comparison will depend on the :url option
10
10
  #
11
11
  # @!macro current_path_query_params
12
- # @overload $0(string, options = {})
12
+ # @overload $0(string, **options)
13
13
  # @param string [String] The string that the current 'path' should equal
14
- # @overload $0(regexp, options = {})
14
+ # @overload $0(regexp, **options)
15
15
  # @param regexp [Regexp] The regexp that the current 'path' should match to
16
16
  # @option options [Boolean] :url (true if `string` ia a full url, otherwise false) Whether the compare should be done against the full current url or just the path
17
17
  # @option options [Boolean] :ignore_query (false) Whether the query portion of the current url/path should be ignored
@@ -49,7 +49,7 @@ module Capybara
49
49
  def has_current_path?(path, **options)
50
50
  assert_current_path(path, options)
51
51
  rescue Capybara::ExpectationNotMet
52
- return false
52
+ false
53
53
  end
54
54
 
55
55
  ##
@@ -64,7 +64,7 @@ module Capybara
64
64
  def has_no_current_path?(path, **options)
65
65
  assert_no_current_path(path, options)
66
66
  rescue Capybara::ExpectationNotMet
67
- return false
67
+ false
68
68
  end
69
69
 
70
70
  private
@@ -74,7 +74,7 @@ module Capybara
74
74
  document.synchronize(query.wait) do
75
75
  yield(query)
76
76
  end
77
- return true
77
+ true
78
78
  end
79
79
  end
80
80
  end
@@ -76,7 +76,7 @@ Capybara::SpecHelper.spec '#assert_text' do
76
76
  @session.visit('/with_html')
77
77
  expect do
78
78
  @session.assert_text(/xxxxyzzz/)
79
- end.to raise_error(Capybara::ExpectationNotMet, /\Aexpected to find text matching \/xxxxyzzz\/ in "This is a test\\nHeader Class(.+)"\Z/)
79
+ end.to raise_error(Capybara::ExpectationNotMet, %r{\Aexpected to find text matching /xxxxyzzz/ in "This is a test\\nHeader Class(.+)"\Z})
80
80
  end
81
81
 
82
82
  it "should escape any characters that would have special meaning in a regexp" do
@@ -7,19 +7,17 @@ Capybara::SpecHelper.spec '#body' do
7
7
  expect(@session.body).to include('Hello world!')
8
8
  end
9
9
 
10
- if "".respond_to?(:encoding)
11
- context "encoding of response between ascii and utf8" do
12
- it "should be valid with html entities" do
13
- @session.visit('/with_html_entities')
14
- expect(@session).to have_content('Encoding') # wait for content to appear if visit is async
15
- expect { @session.body.encode!("UTF-8") }.not_to raise_error
16
- end
10
+ context "encoding of response between ascii and utf8" do
11
+ it "should be valid with html entities" do
12
+ @session.visit('/with_html_entities')
13
+ expect(@session).to have_content('Encoding') # wait for content to appear if visit is async
14
+ expect { @session.body.encode!("UTF-8") }.not_to raise_error
15
+ end
17
16
 
18
- it "should be valid without html entities" do
19
- @session.visit('/with_html')
20
- expect(@session).to have_content('This is a test') # wait for content to appear if visit is async
21
- expect { @session.body.encode!("UTF-8") }.not_to raise_error
22
- end
17
+ it "should be valid without html entities" do
18
+ @session.visit('/with_html')
19
+ expect(@session).to have_content('This is a test') # wait for content to appear if visit is async
20
+ expect { @session.body.encode!("UTF-8") }.not_to raise_error
23
21
  end
24
22
  end
25
23
  end
@@ -93,12 +93,12 @@ Capybara::SpecHelper.spec '#click_link' do
93
93
  end
94
94
 
95
95
  it "should find a link matching an exact regex pattern" do
96
- @session.click_link('labore', href: /\/with_simple_html/)
96
+ @session.click_link('labore', href: %r{/with_simple_html})
97
97
  expect(@session).to have_content('Bar')
98
98
  end
99
99
 
100
100
  it "should find a link matching a partial regex pattern" do
101
- @session.click_link('labore', href: /\/with_simple/)
101
+ @session.click_link('labore', href: %r{/with_simple})
102
102
  expect(@session).to have_content('Bar')
103
103
  end
104
104
 
@@ -137,7 +137,7 @@ Capybara::SpecHelper.spec '#click_link' do
137
137
 
138
138
  it "should follow redirects back to itself" do
139
139
  @session.click_link('BackToMyself')
140
- expect(@session).to have_css('#referrer', text: /\/with_html$/)
140
+ expect(@session).to have_css('#referrer', text: %r{/with_html$})
141
141
  expect(@session).to have_content('This is a test')
142
142
  end
143
143
 
@@ -28,7 +28,7 @@ Capybara::SpecHelper.spec '#assert_matches_selector' do
28
28
  end
29
29
 
30
30
  it "should not accept count options" do
31
- expect { @element.assert_matches_selector(:css, '.number', count: 1) }.to raise_error(ArgumentError)
31
+ expect { @element.assert_matches_selector(:css, '.number', count: 1) }.to raise_error(ArgumentError, /count/)
32
32
  end
33
33
 
34
34
  it "should accept a filter block" do
@@ -159,9 +159,18 @@ Capybara::SpecHelper.spec "#fill_in" do
159
159
  expect(extract_results(@session)['zipcode']).to eq('12345')
160
160
  end
161
161
 
162
+ it "fills in a field if default_set_options is nil" do
163
+ Capybara.default_set_options = nil
164
+ @session.fill_in(:form_first_name, with: 'Thomas')
165
+ @session.click_button('awesome')
166
+ expect(extract_results(@session)['first_name']).to eq('Thomas')
167
+ end
168
+
162
169
  context 'on a pre-populated textfield with a reformatting onchange', requires: [:js] do
163
170
  it 'should only trigger onchange once' do
164
171
  @session.visit('/with_js')
172
+ # Click somewhere on the page to ensure focus is acquired. Without this FF won't generate change events for some reason???
173
+ @session.find(:css, 'body').click
165
174
  @session.fill_in('with_change_event', with: 'some value')
166
175
  # click outside the field to trigger the change event
167
176
  @session.find(:css, 'body').click
@@ -39,7 +39,7 @@ Capybara::SpecHelper.spec '#find_field' do
39
39
  it "should raise error if filter option is invalid" do
40
40
  expect do
41
41
  @session.find_field('Dog', disabled: nil)
42
- end.to raise_error ArgumentError, "Invalid value nil passed to filter disabled"
42
+ end.to raise_error ArgumentError, "Invalid value nil passed to NodeFilter disabled"
43
43
  end
44
44
 
45
45
  context "with :exact option" do
@@ -127,7 +127,8 @@ Capybara::SpecHelper.spec '#find' do
127
127
  before do
128
128
  Capybara.add_selector(:beatle) do
129
129
  xpath { |name| ".//li[contains(@class, 'beatle')][contains(text(), '#{name}')]" }
130
- filter(:type) { |node, type| node[:class].split(/\s+/).include?(type) }
130
+ node_filter(:type) { |node, type| node[:class].split(/\s+/).include?(type) }
131
+ node_filter(:fail) { |_node, _val| raise Capybara::ElementNotFound, 'fail' }
131
132
  end
132
133
  end
133
134
 
@@ -145,13 +146,17 @@ Capybara::SpecHelper.spec '#find' do
145
146
  expect { @session.find(:beatle, 'John', type: 'drummer') }.to raise_error(Capybara::ElementNotFound)
146
147
  expect { @session.find(:beatle, 'George', type: 'drummer') }.to raise_error(Capybara::ElementNotFound)
147
148
  end
149
+
150
+ it "should not raise an ElementNotFound error from in a filter" do
151
+ expect { @session.find(:beatle, 'John', fail: 'something') }.to raise_error(Capybara::ElementNotFound, /beatle "John"/)
152
+ end
148
153
  end
149
154
 
150
155
  context "with custom selector with custom filter and default" do
151
156
  before do
152
157
  Capybara.add_selector(:beatle) do
153
158
  xpath { |name| ".//li[contains(@class, 'beatle')][contains(text(), '#{name}')]" }
154
- filter(:type, default: "drummer") { |node, type| node[:class].split(/\s+/).include?(type) }
159
+ node_filter(:type, default: "drummer") { |node, type| node[:class].split(/\s+/).include?(type) }
155
160
  end
156
161
  end
157
162
 
@@ -174,7 +179,7 @@ Capybara::SpecHelper.spec '#find' do
174
179
  context "with alternate filter set" do
175
180
  before do
176
181
  Capybara::Selector::FilterSet.add(:value) do
177
- filter(:with) { |node, with| node.value == with.to_s }
182
+ node_filter(:with) { |node, with| node.value == with.to_s }
178
183
  end
179
184
 
180
185
  Capybara.add_selector(:id_with_field_filters) do
@@ -10,7 +10,7 @@ Capybara::SpecHelper.spec '#has_link?' do
10
10
  expect(@session).to have_link('awesome title')
11
11
  expect(@session).to have_link('A link', href: '/with_simple_html')
12
12
  expect(@session).to have_link(:'A link', href: :'/with_simple_html')
13
- expect(@session).to have_link('A link', href: /\/with_simple_html/)
13
+ expect(@session).to have_link('A link', href: %r{/with_simple_html})
14
14
  end
15
15
 
16
16
  it "should be false if the given link is not on the page" do
@@ -34,6 +34,6 @@ Capybara::SpecHelper.spec '#has_no_link?' do
34
34
  it "should be true if the given link is not on the page" do
35
35
  expect(@session).to have_no_link('monkey')
36
36
  expect(@session).to have_no_link('A link', href: '/nonexistent-href')
37
- expect(@session).to have_no_link('A link', href: /\/nonexistent-href/)
37
+ expect(@session).to have_no_link('A link', href: %r{/nonexistent-href})
38
38
  end
39
39
  end
@@ -102,6 +102,14 @@ Capybara::SpecHelper.spec "node" do
102
102
  expect { @session.first('//textarea[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
103
103
  end
104
104
 
105
+ it 'should use global default options' do
106
+ Capybara.default_set_options = { clear: :backspace }
107
+ element = @session.first(:fillable_field, type: 'text')
108
+ allow(element.base).to receive(:set)
109
+ element.set('gorilla')
110
+ expect(element.base).to have_received(:set).with('gorilla', clear: :backspace)
111
+ end
112
+
105
113
  context "with a contenteditable element", requires: [:js] do
106
114
  it 'should allow me to change the contents' do
107
115
  @session.visit('/with_js')
@@ -447,6 +455,48 @@ Capybara::SpecHelper.spec "node" do
447
455
  end
448
456
  end
449
457
 
458
+ describe "#execute_script", requires: %i[js es_args] do
459
+ it "should execute the given script in the context of the element and return nothing" do
460
+ @session.visit('/with_js')
461
+ expect(@session.find(:css, '#change').execute_script("this.textContent = 'Funky Doodle'")).to be_nil
462
+ expect(@session).to have_css('#change', text: 'Funky Doodle')
463
+ end
464
+
465
+ it "should pass arguments to the script" do
466
+ @session.visit('/with_js')
467
+ @session.find(:css, '#change').execute_script("this.textContent = arguments[0]", "Doodle Funk")
468
+ expect(@session).to have_css('#change', text: 'Doodle Funk')
469
+ end
470
+ end
471
+
472
+ describe "#evaluate_script", requires: %i[js es_args] do
473
+ it "should evaluate the given script in the context of the element and return whatever it produces" do
474
+ @session.visit('/with_js')
475
+ el = @session.find(:css, '#with_change_event')
476
+ expect(el.evaluate_script("this.value")).to eq('default value')
477
+ end
478
+
479
+ it "should pass arguments to the script" do
480
+ @session.visit('/with_js')
481
+ @session.find(:css, '#change').evaluate_script("this.textContent = arguments[0]", "Doodle Funk")
482
+ expect(@session).to have_css('#change', text: 'Doodle Funk')
483
+ end
484
+
485
+ it "should pass multiple arguments" do
486
+ @session.visit('/with_js')
487
+ change = @session.find(:css, '#change')
488
+ expect(change.evaluate_script("arguments[0] + arguments[1]", 2, 3)).to eq 5
489
+ end
490
+
491
+ it "should support returning elements" do
492
+ @session.visit('/with_js')
493
+ change = @session.find(:css, '#change') # ensure page has loaded and element is available
494
+ el = change.evaluate_script("this")
495
+ expect(el).to be_instance_of(Capybara::Node::Element)
496
+ expect(el).to eq(change)
497
+ end
498
+ end
499
+
450
500
  describe '#reload', requires: [:js] do
451
501
  context "without automatic reload" do
452
502
  before { Capybara.automatic_reload = false }