capybara 2.12.1 → 2.13.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.
@@ -57,7 +57,7 @@ module Capybara
57
57
  options[:text]
58
58
  else
59
59
  if exact_text == true
60
- "\\A#{Regexp.escape(options[:text].to_s)}\\z"
60
+ /\A#{Regexp.escape(options[:text].to_s)}\z/
61
61
  else
62
62
  Regexp.escape(options[:text].to_s)
63
63
  end
@@ -68,7 +68,7 @@ module Capybara
68
68
  end
69
69
 
70
70
  if exact_text.is_a?(String)
71
- regexp = "\\A#{Regexp.escape(options[:exact_text])}\\z"
71
+ regexp = /\A#{Regexp.escape(options[:exact_text])}\z/
72
72
  text_visible = visible
73
73
  text_visible = :all if text_visible == :hidden
74
74
  return false if not node.text(text_visible).match(regexp)
@@ -17,6 +17,12 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
17
17
  end
18
18
 
19
19
  def set(value)
20
+ return if disabled?
21
+ if readonly?
22
+ warn "Attempt to set readonly element with value: #{value} \n * This will raise an exception in a future version of Capybara"
23
+ return
24
+ end
25
+
20
26
  if (Array === value) && !multiple?
21
27
  raise TypeError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
22
28
  end
@@ -28,11 +34,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
28
34
  elsif input_field?
29
35
  set_input(value)
30
36
  elsif textarea?
31
- if self[:readonly]
32
- warn "Attempt to set readonly element with value: #{value} \n * This will raise an exception in a future version of Capybara"
33
- else
34
- native['_capybara_raw_value'] = value.to_s
35
- end
37
+ native['_capybara_raw_value'] = value.to_s
36
38
  end
37
39
  end
38
40
 
@@ -60,6 +62,8 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
60
62
  ((tag_name == 'button') and type.nil? or type == "submit")
61
63
  associated_form = form
62
64
  Capybara::RackTest::Form.new(driver, associated_form).submit(self) if associated_form
65
+ elsif (tag_name == 'input' and %w(checkbox radio).include?(type))
66
+ set(!checked?)
63
67
  elsif (tag_name == 'label')
64
68
  labelled_control = if native[:for]
65
69
  find_xpath("//input[@id='#{native[:for]}']").first
@@ -181,7 +185,7 @@ private
181
185
  end
182
186
  native.remove
183
187
  else
184
- if self[:readonly]
188
+ if readonly?
185
189
  warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"
186
190
  else
187
191
  native['value'] = value.to_s
@@ -91,7 +91,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
91
91
  end
92
92
 
93
93
  def evaluate_script(script, *args)
94
- browser.execute_script("return #{script}", *args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg} )
94
+ result = execute_script("return #{script}", *args)
95
+ unwrap_script_result(result)
95
96
  end
96
97
 
97
98
  def save_screenshot(path, _options={})
@@ -325,4 +326,17 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
325
326
  def silenced_unknown_error_messages
326
327
  [ /Error communicating with the remote browser/ ]
327
328
  end
329
+
330
+ def unwrap_script_result(arg)
331
+ case arg
332
+ when Array
333
+ arg.map { |e| unwrap_script_result(e) }
334
+ when Hash
335
+ arg.each { |k, v| arg[k] = unwrap_script_result(v) }
336
+ when Selenium::WebDriver::Element
337
+ Capybara::Selenium::Node.new(self, arg)
338
+ else
339
+ arg
340
+ end
341
+ end
328
342
  end
@@ -6,7 +6,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
6
6
  end
7
7
 
8
8
  def all_text
9
- text = driver.browser.execute_script("return arguments[0].textContent", native)
9
+ text = driver.execute_script("return arguments[0].textContent", self)
10
10
  Capybara::Helpers.normalize_whitespace(text)
11
11
  end
12
12
 
@@ -70,20 +70,29 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
70
70
  # Clear field by JavaScript assignment of the value property.
71
71
  # Script can change a readonly element which user input cannot, so
72
72
  # don't execute if readonly.
73
- driver.browser.execute_script "arguments[0].value = ''", native
73
+ driver.execute_script "arguments[0].value = ''", self
74
74
  native.send_keys(value.to_s)
75
75
  end
76
76
  end
77
77
  elsif native.attribute('isContentEditable')
78
78
  #ensure we are focused on the element
79
+ native.click
79
80
  script = <<-JS
80
81
  var range = document.createRange();
81
82
  arguments[0].focus();
82
83
  range.selectNodeContents(arguments[0]);
83
84
  window.getSelection().addRange(range);
84
85
  JS
85
- driver.browser.execute_script script, native
86
- native.send_keys(value.to_s)
86
+ driver.execute_script script, self
87
+ if (driver.options[:browser].to_s == "chrome") ||
88
+ (driver.options[:browser].to_s == "firefox" && !driver.marionette?)
89
+ # chromedriver raises a can't focus element if we use native.send_keys
90
+ # we've already focused it so just use action api
91
+ driver.browser.action.send_keys(value.to_s).perform
92
+ else
93
+ # action api is really slow here just use native.send_keys
94
+ native.send_keys(value.to_s)
95
+ end
87
96
  end
88
97
  end
89
98
 
@@ -123,17 +123,19 @@ module Capybara
123
123
  # Raise errors encountered in the server
124
124
  #
125
125
  def raise_server_error!
126
- if Capybara.raise_server_errors and @server and @server.error
126
+ if @server and @server.error
127
127
  # Force an explanation for the error being raised as the exception cause
128
128
  begin
129
- raise CapybaraError, "Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true"
129
+ if Capybara.raise_server_errors
130
+ raise CapybaraError, "Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true"
131
+ end
130
132
  rescue CapybaraError
131
133
  #needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
132
134
  raise @server.error, @server.error.message, @server.error.backtrace
135
+ ensure
136
+ @server.reset_error!
133
137
  end
134
138
  end
135
- ensure
136
- @server.reset_error! if @server
137
139
  end
138
140
 
139
141
  ##
@@ -387,21 +389,7 @@ module Capybara
387
389
  # @overload within_frame(index)
388
390
  # @param [Integer] index index of a frame (0 based)
389
391
  def within_frame(*args)
390
- frame = within(document) do # Previous 2.x versions ignored current scope when finding frames - consider changing in 3.0
391
- case args[0]
392
- when Capybara::Node::Element
393
- args[0]
394
- when String, Hash
395
- find(:frame, *args)
396
- when Symbol
397
- find(*args)
398
- when Integer
399
- idx = args[0]
400
- all(:frame, minimum: idx+1)[idx]
401
- else
402
- raise TypeError
403
- end
404
- end
392
+ frame = _find_frame(*args)
405
393
 
406
394
  begin
407
395
  switch_to_frame(frame)
@@ -612,10 +600,10 @@ module Capybara
612
600
  #
613
601
  def execute_script(script, *args)
614
602
  @touched = true
615
- if driver.method(:execute_script).arity == 1
616
- raise Capybara::NotSupportedByDriverError, "The current driver does not support arguments being passed with execute_script" unless args.empty?
603
+ if args.empty?
617
604
  driver.execute_script(script)
618
605
  else
606
+ raise Capybara::NotSupportedByDriverError, "The current driver does not support execute_script arguments" if driver.method(:execute_script).arity == 1
619
607
  driver.execute_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
620
608
  end
621
609
  end
@@ -631,12 +619,13 @@ module Capybara
631
619
  #
632
620
  def evaluate_script(script, *args)
633
621
  @touched = true
634
- if driver.method(:evaluate_script).arity == 1
635
- raise Capybara::NotSupportedByDriverError, "The current driver does not support arguments being passed with execute_script" unless args.empty?
622
+ result = if args.empty?
636
623
  driver.evaluate_script(script)
637
624
  else
625
+ raise Capybara::NotSupportedByDriverError, "The current driver does not support evaluate_script arguments" if driver.method(:evaluate_script).arity == 1
638
626
  driver.evaluate_script(script, *args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg} )
639
627
  end
628
+ element_script_result(result)
640
629
  end
641
630
 
642
631
  ##
@@ -842,5 +831,36 @@ module Capybara
842
831
  def scopes
843
832
  @scopes ||= [nil]
844
833
  end
834
+
835
+ def element_script_result(arg)
836
+ case arg
837
+ when Array
838
+ arg.map { |e| element_script_result(e) }
839
+ when Hash
840
+ arg.each { |k, v| arg[k] = element_script_result(v) }
841
+ when Capybara::Driver::Node
842
+ Capybara::Node::Element.new(self, arg, nil, nil)
843
+ else
844
+ arg
845
+ end
846
+ end
847
+
848
+ def _find_frame(*args)
849
+ within(document) do # Previous 2.x versions ignored current scope when finding frames - consider changing in 3.0
850
+ case args[0]
851
+ when Capybara::Node::Element
852
+ args[0]
853
+ when String, Hash
854
+ find(:frame, *args)
855
+ when Symbol
856
+ find(*args)
857
+ when Integer
858
+ idx = args[0]
859
+ all(:frame, minimum: idx+1)[idx]
860
+ else
861
+ raise TypeError
862
+ end
863
+ end
864
+ end
845
865
  end
846
866
  end
@@ -19,7 +19,6 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
19
19
  end
20
20
 
21
21
  it "should accept the prompt with a response" do
22
- pending "selenium-webdriver/geckodriver doesn't support sending a response to prompts" if marionette?(@session)
23
22
  @session.accept_prompt with: 'the response' do
24
23
  @session.click_link('Open prompt')
25
24
  end
@@ -27,7 +26,6 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
27
26
  end
28
27
 
29
28
  it "should accept the prompt if the message matches" do
30
- pending "selenium-webdriver/geckodriver doesn't support sending a response to prompts" if marionette?(@session)
31
29
  @session.accept_prompt 'Prompt opened', with: 'matched' do
32
30
  @session.click_link('Open prompt')
33
31
  end
@@ -44,7 +42,6 @@ Capybara::SpecHelper.spec '#accept_prompt', requires: [:modals] do
44
42
 
45
43
 
46
44
  it "should return the message presented" do
47
- pending "selenium-webdriver/geckodriver doesn't support sending a response to prompts" if marionette?(@session)
48
45
  message = @session.accept_prompt with: 'the response' do
49
46
  @session.click_link('Open prompt')
50
47
  end
@@ -18,4 +18,11 @@ Capybara::SpecHelper.spec "#evaluate_script", requires: [:js] do
18
18
  expect(@session).to have_css('#change', text: 'Doodle Funk')
19
19
  end
20
20
 
21
+ it "should support returning elements", requires: [:js, :es_args] do
22
+ @session.visit('/with_js')
23
+ el = @session.find(:css, '#change')
24
+ el = @session.evaluate_script("document.getElementById('change')")
25
+ expect(el).to be_instance_of(Capybara::Node::Element)
26
+ expect(el).to eq(@session.find(:css, '#change'))
27
+ end
21
28
  end
@@ -135,10 +135,10 @@ Capybara::SpecHelper.spec "node" do
135
135
  end
136
136
 
137
137
  it 'should allow me to change the contents of a contenteditable elements child', requires: [:js] do
138
- pending "Selenium doesn't like editing nested contents" if @session.respond_to?(:mode) && @session.mode.to_s =~ /^selenium_/
139
138
  @session.visit('/with_js')
140
139
  @session.find(:css,'#existing_content_editable_child').set('WYSIWYG')
141
140
  expect(@session.find(:css,'#existing_content_editable_child').text).to eq('WYSIWYG')
141
+ expect(@session.find(:css,'#existing_content_editable_child_parent').text).to eq('Some content WYSIWYG')
142
142
  end
143
143
  end
144
144
 
@@ -313,6 +313,30 @@ Capybara::SpecHelper.spec "node" do
313
313
  @session.find(:css, '#link_placeholder').click
314
314
  expect(@session.current_url).to match(%r{/with_html$})
315
315
  end
316
+
317
+ it "should be able to check a checkbox" do
318
+ @session.visit('form')
319
+ cbox = @session.find(:checkbox, 'form_terms_of_use')
320
+ expect(cbox).not_to be_checked
321
+ cbox.click
322
+ expect(cbox).to be_checked
323
+ end
324
+
325
+ it "should be able to uncheck a checkbox" do
326
+ @session.visit('/form')
327
+ cbox = @session.find(:checkbox, 'form_pets_dog')
328
+ expect(cbox).to be_checked
329
+ cbox.click
330
+ expect(cbox).not_to be_checked
331
+ end
332
+
333
+ it "should be able to select a radio button" do
334
+ @session.visit('/form')
335
+ radio = @session.find(:radio_button, 'gender_male')
336
+ expect(radio).not_to be_checked
337
+ radio.click
338
+ expect(radio).to be_checked
339
+ end
316
340
  end
317
341
 
318
342
  describe '#double_click', requires: [:js] do
@@ -30,7 +30,7 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do
30
30
  window = (@session.windows - [@window]).first
31
31
  expect(@session.driver).to receive(:switch_to_window).exactly(5).times.and_call_original
32
32
  @session.within_window window do
33
- expect(['Title of the first popup', 'Title of popup two']).to include(@session.title)
33
+ expect(@session).to have_title(/Title of the first popup|Title of popup two/)
34
34
  end
35
35
  expect(@session.title).to eq('With Windows')
36
36
  end
@@ -140,7 +140,7 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do
140
140
  it "should find window by handle" do
141
141
  window = (@session.windows - [@window]).first
142
142
  @session.within_window window.handle do
143
- expect(['Title of the first popup', 'Title of popup two']).to include(@session.title)
143
+ expect(@session).to have_title(/Title of the first popup|Title of popup two/)
144
144
  end
145
145
  end
146
146
 
@@ -40,7 +40,8 @@
40
40
  <p>
41
41
  <div contenteditable='true' id='existing_content_editable'>Editable content</div>
42
42
  <div contenteditable='true' id='blank_content_editable' style='height: 1em;'></div>
43
- <div contenteditable='true' style='height: 1em;'>
43
+ <div contenteditable='true' id='existing_content_editable_child_parent' style='height: 1em;'>
44
+ Some content
44
45
  <div id='existing_content_editable_child' style='height: 1em;'>Content</div>
45
46
  </div>
46
47
  </p>
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Capybara
3
- VERSION = '2.12.1'
3
+ VERSION = '2.13.0'
4
4
  end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+ require 'capybara/minitest'
4
+
5
+ class MinitestTest < Minitest::Test
6
+ include Capybara::DSL
7
+ include Capybara::Minitest::Assertions
8
+
9
+ def setup
10
+ visit('/form')
11
+ end
12
+
13
+ def teardown
14
+ Capybara.reset_sessions!
15
+ end
16
+
17
+ def test_assert_text
18
+ assert_text('Form')
19
+ assert_no_text('Not on the page')
20
+ refute_text('Also Not on the page')
21
+ end
22
+
23
+ def test_assert_title
24
+ visit('/with_title')
25
+ assert_title('Test Title')
26
+ assert_no_title('Not the title')
27
+ refute_title('Not the title')
28
+ end
29
+
30
+ def test_assert_current_path
31
+ assert_current_path('/form')
32
+ assert_no_current_path('/not_form')
33
+ refute_current_path('/not_form')
34
+ end
35
+
36
+ def test_assert_xpath
37
+ assert_xpath('.//select[@id="form_title"]')
38
+ assert_xpath('.//select', count: 1) { |el| el[:id] == "form_title" }
39
+ assert_no_xpath('.//select[@id="not_form_title"]')
40
+ assert_no_xpath('.//select') { |el| el[:id] == "not_form_title"}
41
+ refute_xpath('.//select[@id="not_form_title"]')
42
+ end
43
+
44
+ def test_assert_css
45
+ assert_css('select#form_title')
46
+ assert_no_css('select#not_form_title')
47
+ end
48
+
49
+ def test_assert_link
50
+ visit('/with_html')
51
+ assert_link('A link')
52
+ assert_link(count: 1){ |el| el.text == 'A link'}
53
+ assert_no_link('Not on page')
54
+ end
55
+
56
+ def test_assert_button
57
+ assert_button('fresh_btn')
58
+ assert_button(count: 1){ |el| el[:id] == 'fresh_btn' }
59
+ assert_no_button('not_btn')
60
+ end
61
+
62
+ def test_assert_field
63
+ assert_field('customer_email')
64
+ assert_no_field('not_on_the_form')
65
+ end
66
+
67
+ def test_assert_select
68
+ assert_select('form_title')
69
+ assert_no_select('not_form_title')
70
+ end
71
+
72
+ def test_assert_checked_field
73
+ assert_checked_field('form_pets_dog')
74
+ assert_no_checked_field('form_pets_cat')
75
+ refute_checked_field('form_pets_snake')
76
+ end
77
+
78
+ def test_assert_unchecked_field
79
+ assert_unchecked_field('form_pets_cat')
80
+ assert_no_unchecked_field('form_pets_dog')
81
+ refute_unchecked_field('form_pets_snake')
82
+ end
83
+
84
+ def test_assert_table
85
+ visit('/tables')
86
+ assert_table('agent_table')
87
+ assert_no_table('not_on_form')
88
+ refute_table('not_on_form')
89
+ end
90
+
91
+ def test_assert_matches_selector
92
+ assert_matches_selector(find(:field, 'customer_email'), :field, 'customer_email')
93
+ assert_not_matches_selector(find(:select, 'form_title'), :field, 'customer_email')
94
+ refute_matches_selector(find(:select, 'form_title'), :field, 'customer_email')
95
+ end
96
+
97
+ def test_assert_matches_css
98
+ assert_matches_css(find(:select, 'form_title'), 'select#form_title')
99
+ refute_matches_css(find(:select, 'form_title'), 'select#form_other_title')
100
+ end
101
+
102
+ def test_assert_matches_xpath
103
+ assert_matches_xpath(find(:select, 'form_title'), './/select[@id="form_title"]')
104
+ refute_matches_xpath(find(:select, 'form_title'), './/select[@id="form_other_title"]')
105
+ end
106
+ end
107
+
108
+ RSpec.describe 'capybara/minitest' do
109
+ before do
110
+ Capybara.current_driver = :rack_test
111
+ Capybara.app = TestApp
112
+ end
113
+
114
+ it "should support minitest" do
115
+ output = StringIO.new
116
+ reporter = Minitest::SummaryReporter.new(output)
117
+ reporter.start
118
+ MinitestTest.run reporter, {}
119
+ reporter.report
120
+ expect(output.string).to include("15 runs, 42 assertions, 0 failures, 0 errors, 0 skips")
121
+ end
122
+ end