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.
- checksums.yaml +4 -4
- data/History.md +12 -0
- data/README.md +31 -9
- data/lib/capybara/minitest.rb +297 -0
- data/lib/capybara/minitest/spec.rb +200 -0
- data/lib/capybara/queries/selector_query.rb +2 -2
- data/lib/capybara/rack_test/node.rb +10 -6
- data/lib/capybara/selenium/driver.rb +15 -1
- data/lib/capybara/selenium/node.rb +13 -4
- data/lib/capybara/session.rb +43 -23
- data/lib/capybara/spec/session/accept_prompt_spec.rb +0 -3
- data/lib/capybara/spec/session/evaluate_script_spec.rb +7 -0
- data/lib/capybara/spec/session/node_spec.rb +25 -1
- data/lib/capybara/spec/session/window/within_window_spec.rb +2 -2
- data/lib/capybara/spec/views/with_js.erb +2 -1
- data/lib/capybara/version.rb +1 -1
- data/spec/minitest_spec.rb +122 -0
- data/spec/minitest_spec_spec.rb +121 -0
- data/spec/shared_selenium_session.rb +28 -0
- metadata +26 -8
@@ -57,7 +57,7 @@ module Capybara
|
|
57
57
|
options[:text]
|
58
58
|
else
|
59
59
|
if exact_text == true
|
60
|
-
|
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 =
|
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
|
-
|
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
|
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
|
-
|
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.
|
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.
|
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.
|
86
|
-
|
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
|
|
data/lib/capybara/session.rb
CHANGED
@@ -123,17 +123,19 @@ module Capybara
|
|
123
123
|
# Raise errors encountered in the server
|
124
124
|
#
|
125
125
|
def raise_server_error!
|
126
|
-
if
|
126
|
+
if @server and @server.error
|
127
127
|
# Force an explanation for the error being raised as the exception cause
|
128
128
|
begin
|
129
|
-
|
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 =
|
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
|
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
|
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(
|
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(
|
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>
|
data/lib/capybara/version.rb
CHANGED
@@ -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
|