When using rack-test 2, we no longer need to send NilUploadedFile to trigger multipart

This commit is contained in:
Thomas Walpole 2022-06-12 11:05:22 -07:00
parent 6cba8b4b5a
commit a3ff5514f4
9 changed files with 76 additions and 11 deletions

View File

@ -7,6 +7,8 @@ gemspec
gem 'xpath', github: 'teamcapybara/xpath'
# gem 'rack-test', github: 'rack/rack-test'
group :doc do
gem 'redcarpet', platforms: :mri
end

View File

@ -31,11 +31,17 @@ class Capybara::RackTest::Browser
request(last_request.fullpath, last_request.env)
end
def submit(method, path, attributes)
def submit(method, path, attributes, content_type: nil)
path = request_path if path.nil? || path.empty?
uri = build_uri(path)
uri.query = '' if method.to_s.casecmp('get').zero?
process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => referer_url)
process_and_follow_redirects(
method,
uri.to_s,
attributes,
'HTTP_REFERER' => referer_url,
'CONTENT_TYPE' => content_type
)
end
def follow(method, path, **attributes)

View File

@ -30,19 +30,31 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
form_elements = native.xpath(form_elements_xpath).reject { |el| submitter?(el) && (el != button.native) }
form_elements.each_with_object(make_params) do |field, params|
form_params = form_elements.each_with_object({}.compare_by_identity) do |field, params|
case field.name
when 'input', 'button' then add_input_param(field, params)
when 'select' then add_select_param(field, params)
when 'textarea' then add_textarea_param(field, params)
end
end
form_params.each_with_object(make_params) do |(name, value), params|
merge_param!(params, name, value)
end.to_params_hash
# form_elements.each_with_object(make_params) do |field, params|
# case field.name
# when 'input', 'button' then add_input_param(field, params)
# when 'select' then add_select_param(field, params)
# when 'textarea' then add_textarea_param(field, params)
# end
# end.to_params_hash
end
def submit(button)
action = button&.[]('formaction') || native['action']
method = button&.[]('formmethod') || request_method
driver.submit(method, action.to_s, params(button))
driver.submit(method, action.to_s, params(button), content_type: native['enctype'])
end
def multipart?
@ -88,6 +100,8 @@ private
Capybara::RackTest::Node.new(driver, field).value.to_s
when 'file'
return if value.empty? && params.keys.include?(name) && Rack::Test::VERSION.to_f >= 2.0 # rubocop:disable Performance/InefficientHashSearch
if multipart?
file_to_upload(value)
else
@ -96,7 +110,8 @@ private
else
value
end
merge_param!(params, name, value)
# merge_param!(params, name, value)
params[name] = value
end
def file_to_upload(filename)
@ -109,18 +124,23 @@ private
end
def add_select_param(field, params)
name = field['name']
if field.has_attribute?('multiple')
field.xpath('.//option[@selected]').each do |option|
merge_param!(params, field['name'], (option['value'] || option.text).to_s)
value = field.xpath('.//option[@selected]').map do |option|
# merge_param!(params, field['name'], (option['value'] || option.text).to_s)
(option['value'] || option.text).to_s
end
params[name] = value unless value.empty?
else
option = field.xpath('.//option[@selected]').first || field.xpath('.//option').first
merge_param!(params, field['name'], (option['value'] || option.text).to_s) if option
# merge_param!(params, field['name'], (option['value'] || option.text).to_s) if option
params[name] = (option['value'] || option.text).to_s if option
end
end
def add_textarea_param(field, params)
merge_param!(params, field['name'], field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n"))
# merge_param!(params, field['name'], field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n"))
params[field['name']] = field['_capybara_raw_value'].to_s.gsub(/\r?\n/, "\r\n")
end
def submitter?(el)

View File

@ -55,6 +55,12 @@ Capybara::SpecHelper.spec '#attach_file' do
expect(@session).to have_content('No file uploaded')
end
it 'should send prior hidden field if no file submitted' do
@session.click_button('Upload Empty With Hidden')
expect(extract_results(@session)['document2']).to eq('hidden_field')
expect(extract_content_type(@session)).to start_with('multipart/form-data;')
end
it 'should send content type text/plain when uploading a text file' do
@session.attach_file 'Single Document', with_os_path_separators(test_file_path)
@session.click_button 'Upload Single'

View File

@ -122,6 +122,11 @@ module Capybara
YAML.safe_load results, permitted_classes: perms
end
def extract_content_type(session)
expect(session).to have_xpath("//pre[@id='content_type']")
Capybara::HTML(session.body).xpath("//pre[@id='content_type']").first.text
end
def be_an_invalid_element_error(session)
satisfy { |error| session.driver.invalid_element_errors.any? { |e| error.is_a? e } }
end

View File

@ -256,12 +256,15 @@ class TestApp < Sinatra::Base
post '/form' do
self.class.form_post_count += 1
%(<pre id="results">#{params[:form].merge('post_count' => self.class.form_post_count).to_yaml}</pre>)
%(
<pre id="content_type">#{request.content_type}</pre>
<pre id="results">#{params.fetch(:form, {}).merge('post_count' => self.class.form_post_count).to_yaml}</pre>
)
end
post '/upload_empty' do
if params[:form][:file].nil?
'Successfully ignored empty file field.'
"Successfully ignored empty file field. Content type was #{request.content_type}"
else
'Something went wrong.'
end

View File

@ -586,6 +586,19 @@ New line after and before textarea tag
<p>
</form>
<form action="/form" method="post" enctype="multipart/form-data">
<input type="hidden" name="form[document2]" value="hidden_field"/>
<p>
<label for="form_document">Document with hidden</label>
<input type="file" name="form[document2]" id="form_document2"/>
</p>
<p>
<input type="submit" value="Upload Empty With Hidden"/>
<p>
</form>
<form action="/upload_multiple" method="post" enctype="multipart/form-data">
<p>
<label for="form_multiple_file_name">File Name</label>

View File

@ -15,6 +15,8 @@ Capybara::SpecHelper.run_specs TestClass.new, 'DSL', capybara_skip: %i[
pending "Nokogiri doesn't support case insensitive CSS attribute matchers"
when /#click_button should follow permanent redirects that maintain method/
pending "Rack < 2 doesn't support 308" if Gem.loaded_specs['rack'].version < Gem::Version.new('2.0.0')
when /#attach_file with multipart form should send prior hidden field if no file submitted/
skip 'Rack-test < 2 needs an empty file to detect multipart form' if Gem.loaded_specs['rack-test'].version < Gem::Version.new('2.0.0')
end
end

View File

@ -30,6 +30,8 @@ Capybara::SpecHelper.run_specs TestSessions::RackTest, 'RackTest', capybara_skip
skip "Nokogiri doesn't support case insensitive CSS attribute matchers"
when /#click_button should follow permanent redirects that maintain method/
skip "Rack < 2 doesn't support 308" if Gem.loaded_specs['rack'].version < Gem::Version.new('2.0.0')
when /#attach_file with multipart form should send prior hidden field if no file submitted/
skip 'Rack-test < 2 needs an empty file to detect multipart form' if Gem.loaded_specs['rack-test'].version < Gem::Version.new('2.0.0')
end
end
@ -93,6 +95,12 @@ RSpec.describe Capybara::Session do # rubocop:disable RSpec/MultipleDescribes
session.click_button('Upload Empty')
expect(session.html).to include('Successfully ignored empty file field.')
end
it 'should submit multipart even if no file is submitted' do
session.visit('/form')
session.click_button('Upload Empty')
expect(session.html).to include('Content type was multipart/form-data;')
end
end
it 'should not submit an obsolete mime type' do