ActionController::ParameterMissing when file is attached
Without the file attached, I get validation errors (as expected), but as soon as I attach a file, I get no errors on the console suggesting that's gone wrong, but I get a failed submit with the following error in RSpec:
ActionController::ParameterMissing:
param is missing or the value is empty: guide
Did you mean? action
controller
Removing the validation on the file attribute and not filling it in proves that the submit is successful otherwise.
I'm using a remote chrome docker instance using a browserless.io chrome image
Further to this, it appears that uploading and attaching files via the browserless workspace API works fine, but doesn't if the app directory is bind mounted in to the browserless container. Using VSCode devcontainers on macOS Monterey, so could be anything in the way that directories are bind mounted too causing this issue.
Here's a helper to allow remote uploads to work:
# frozen_string_literal: true
require 'net/http/post/multipart'
require 'mime/types'
# Browserless Helpers
module BrowserlessHelpers
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
def attach_remote_file(locator = nil, paths, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments
raise ArgumentError, '`#attach_file` does not support passing both a locator and a block' if locator && block_given?
remote_paths = []
Array(paths).each do |path|
raise Capybara::FileNotFound, "cannot attach file, #{path} does not exist" unless File.exist?(path.to_s)
# Upload local files to browserless.io instance
remote_paths << browserless_file(path)
end
remote_paths = remote_paths.first if remote_paths.one?
options[:allow_self] = true if locator.nil?
if block_given?
begin
execute_script Capybara::Node::Actions::CAPTURE_FILE_ELEMENT_SCRIPT
yield
file_field = evaluate_script 'window._capybara_clicked_file_input'
raise ArgumentError, "Capybara was unable to determine the file input you're attaching to" unless file_field
rescue ::Capybara::NotSupportedByDriverError
warn 'Block mode of `#attach_remote_file` is not supported by the current driver - ignoring.'
end
end
# Allow user to update the CSS style of the file input since they are so often hidden on a page
if make_visible
ff = file_field || find(:file_field, locator, **options.merge(visible: :all))
while_visible(ff, make_visible) { |el| el.set(remote_paths) }
else
(file_field || find(:file_field, locator, **options)).set(remote_paths)
end
end
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
private
def browserless_file(path)
request = Net::HTTP::Post::Multipart.new('/workspace',
file: UploadIO.new(path.to_s,
mime_type(path)))
uri = URI.parse(ENV['CHROME_URL'])
response = Net::HTTP.start(uri.host, uri.port) { |http| http.request(request) }
json = JSON.parse(response.body, symbolize_names: true).first
json[:path]
end
def mime_type(path)
MIME::Types.type_for(path.to_s).first.content_type
end
end
RSpec.configure do |config|
config.include BrowserlessHelpers, type: :system
end
Replace instances of attach_file with attach_remote_file
@simmerz There are various capybara tests regarding file attachment:
be rspec --example="#attach_file"
Google Chrome 96.0.4664.45 unknown
Capybara starting Puma...
* Version 4.3.10 , codename: Mysterious Traveller
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:36639
Run options: include {:full_description=>/\#attach_file/}
Capybara::Session Cuprite
#attach_file
with normal form
should set a file path by id
should set a file path by label
should be able to set on element if no locator passed
casts to string
with multipart form
should set a file path by id
should set a file path by label
should not break if no file is submitted
should send content type text/plain when uploading a text file
should send content type image/jpeg when uploading an image
should not break when uploading a file without extension
should not break when using HTML5 multiple file input
should not break when using HTML5 multiple file input uploading multiple files
should not send anything when attaching no files to a multiple upload field
should not append files to already attached
should fire change once when uploading multiple files from empty
should fire change once for each set of files uploaded
with a locator that doesn't exist
should raise an error
with a path that doesn't exist
should raise an error
with :exact option
should set a file path by partial label when false
should not allow partial matches when true
with :make_visible option
applies a default style change when true
accepts a hash of styles to be applied
raises an error when the file input is not made visible
resets the style when done
should fire change
with a block
can upload by clicking the file input
can upload by clicking the label
should fire change
Finished in 8.06 seconds (files took 1.3 seconds to load)
28 examples, 0 failures
and all of them pass. You are describing application specific issue and I'm glad to help, but you should at least to provide the failing test in isolation (example dummy app) or try to reproduce the issue inside cuprite tests. Without it it's hard to take a look at BrowserlessHelpers and say where the issue is...
It actually looks like it's a specific issue when the app codebase is bind mounted inside a docker container, rather than a specific problem with cuprite - I'm using VSCode devcontainers here. There are similar issues when attaching files via FactoryBot or directly using fixture_file_upload too, and the solution in those instances is to make a copy of the file first so it's native to the container and then attach that.
The BrowserlessHelpers module is effectively just a remote upload to the workspace API in Browserless, so that Chrome can attach the file natively without needing to be bind mounted to the app folder. It's a working option.
The reason for the failure isn't in that code I pasted - more that was a means of sharing one solution if others were suffering it
In general now it's not clear even to me what's going on because there are a lot of missing details regarding the spec as well as test setup. I don't clearly see your solution either. So I'm afraid currently it's all mixed up and doesn't provide any particular help to the others. If you could restructure your description from top to bottom that would be helpful.