Remote file uploads with Selenium & Capybara

Posted Dec 15th, 2013

Capybara & Seleniumcheck out the Selenium gem source!

# The detector is an object that responds to #call, and when called # will determine if the given string represents a file. If it does, # the path to the file on the local file system should be returned, # otherwise nil or false.

So, as long as the object we set as a file_detector can verify that a passed in string is a file path, and then return that path, we can add uploads to Capybara. Sweet! Kinda. See, there's a catch - Capybara doesn't provide direct access to the Selenium driver object, which is kinda the point of Capybara. We'll have to get access to it:

selenium_driver = page.object.browser

Let's create a file_detector object and pass it to selenium_driver. It needs to respond to 'call'. You know what responds to 'call' and doesn't require us lazy, lazy Ruby programmers to create (and then instantiate) a whole new class? Lambdas.

selenium_driver.file_detector = lambda do |args|
# Check that the first arg is really a file, for realz

Hmm, actually, we might be getting ahead of ourselves. It would suuuuuck to have to change all our tests to find file inputs and call send_keys and all that junk, so let's check out the Capybara DSL method "attach_file" to see what changes we have to make to it:

### lib/capybara/node/actions.rb
def attach_file(locator, path, options={})

    Array(path).each do |p|
        raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
    find(:file_field, locator, options).set(path)

### lib/capybara/selenium/node.rb
  def set(value)
  # SNIP #
  elsif tag_name == 'input' and type == 'file'
      path_names = value.to_s.empty? ? [] : value

Oh, awesome! Check out the highlighted lines -- Capybara will already check that the file path we're providing exists, before it's even passed to the file_detector. That means we don't even need to *do* anything in our file_detector lambda! The entirety of what we need to do to give us remote file uploading in Capybara is below: The Solution

## Allows remote uploads.  Totally awesomesauce (labs).
selenium_driver = page.object.browser

selenium_driver.file_detector = lambda {|args| args.first.to_s}

Isn't Ruby great?

Written by