Method: Capybara::Node::Actions#attach_file

Defined in:
lib/capybara/node/actions.rb

#attach_file([locator], paths, **options) ⇒ Capybara::Node::Element #attach_file(paths) { ... } ⇒ Capybara::Node::Element

Find a descendant file field on the page and attach a file given its path. There are two ways to use #attach_file, in the first method the file field can be found via its name, id, test_id attribute, or label text. In the case of the file field being hidden for styling reasons the make_visible option can be used to temporarily change the CSS of the file field, attach the file, and then revert the CSS back to original. If no locator is passed this will match self or a descendant. The second method, which is currently in beta and may be changed/removed, involves passing a block which performs whatever actions would trigger the file chooser to appear.

# will attach file to a descendant file input element that has a name, id, or label_text matching 'My File'
page.attach_file('My File', '/path/to/file.png')

# will attach file to el if it's a file input element
el.attach_file('/path/to/file.png')

# will attach file to whatever file input is triggered by the block
page.attach_file('/path/to/file.png') do
  page.find('#upload_button').click
end

Overloads:

  • #attach_file([locator], paths, **options) ⇒ Capybara::Node::Element

    If the driver is capable of executing JavaScript, this method will wait for a set amount of time and continuously retry finding the element until either the element is found or the time expires. The length of time this method will wait is controlled through default_max_wait_time.

    Parameters:

    • locator (String)

      Which field to attach the file to

    • paths (String, Array<String>)

      The path(s) of the file(s) that will be attached

    Options Hash (**options):

    • wait (false, true, Numeric)

      Maximum time to wait for matching element to appear. Defaults to default_max_wait_time.

    • match (Symbol)

      The matching strategy to use (:one, :first, :prefer_exact, :smart). Defaults to match.

    • exact (Boolean)

      Match the exact label name/contents or accept a partial match. Defaults to exact.

    • multiple (Boolean)

      Match field which allows multiple file selection

    • id (String, Regexp)

      Match fields that match the id attribute

    • name (String)

      Match fields that match the name attribute

    • class (String, Array<String>, Regexp)

      Match fields that match the class(es) provided

    • make_visible (true, Hash)

      A Hash of CSS styles to change before attempting to attach the file, if true, { opacity: 1, display: 'block', visibility: 'visible' } is used (may not be supported by all drivers).

  • #attach_file(paths) { ... } ⇒ Capybara::Node::Element

    Parameters:

    • paths (String, Array<String>)

      The path(s) of the file(s) that will be attached

    Yields:

    • Block whose actions will trigger the system file chooser to be shown

Returns:


278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/capybara/node/actions.rb', line 278

def attach_file(locator = nil, paths, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments
  if locator && block_given?
    raise ArgumentError, '``#attach_file` does not support passing both a locator and a block'
  end

  Array(paths).each do |path|
    raise Capybara::FileNotFound, "cannot attach file, #{path} does not exist" unless File.exist?(path.to_s)
  end
  options[:allow_self] = true if locator.nil?

  if block_given?
    begin
      execute_script 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_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(paths) }
  else
    (file_field || find(:file_field, locator, **options)).set(paths)
  end
end