teamcapybara / capybara

Acceptance test framework for web applications
http://teamcapybara.github.io/capybara/
MIT License
10.02k stars 1.45k forks source link

File uploads to remote driver #2255

Closed juanca closed 5 years ago

juanca commented 5 years ago

Note: This is for issues with Capybara. If you have a howto type question, please ask on the mailing list as requested in the README: http://groups.google.com/group/ruby-capybara

Meta

Capybara Version: ruby-2.3.3@mavenlink/gems/capybara-3.15.1 Driver Information (and browser if relevant): ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4 with Mozilla Firefox 69.0.2

Expected Behavior

I have a few pre-existing tests which upload a file to our Rails test server from the remote browser. Something like:

attach_file('vendor-csv-import', Rails.root.join(filename), { make_visible: true })

I expect this to just work. However, the thing that works is something like:

selenium_driver = Capybara.page.driver.browser
selenium_driver.file_detector = lambda { |args| args.first.to_s }
attach_file('vendor-csv-import', Rails.root.join(filename), { make_visible: true })

Resource: https://saucelabs.com/blog/remote-file-uploads-with-selenium-capybara

Actual Behavior

The test software does not upload the file correctly. Here is a stack trace:

Failures:

  1) vendor tab on time and expense settings page when there are vendors on the account when importing vendors from a flat file ignores vendors that already exist
     Failure/Error: attach_file('vendor-csv-import', Rails.root.join(filename), { make_visible: true })

     Selenium::WebDriver::Error::InvalidArgumentError:
       File not found: /Users/juanca/workspace/mavenlink/public/example_vendors.csv
     # WebDriverError@chrome://marionette/content/error.js:175:5
     # InvalidArgumentError@chrome://marionette/content/error.js:304:5
     # interaction.uploadFiles@chrome://marionette/content/interaction.js:516:13
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/response.rb:72:in `assert_ok'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/response.rb:34:in `initialize'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/http/common.rb:88:in `new'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/http/common.rb:88:in `create_response'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/http/default.rb:114:in `request'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/http/common.rb:64:in `call'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/bridge.rb:167:in `execute'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/w3c/bridge.rb:567:in `execute'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/remote/w3c/bridge.rb:386:in `send_keys_to_element'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/selenium-webdriver-3.142.4/lib/selenium/webdriver/common/element.rb:156:in `send_keys'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/selenium/node.rb:285:in `set_file'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/selenium/nodes/firefox_node.rb:37:in `set_file'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/selenium/node.rb:67:in `set'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/node/element.rb:119:in `block in set'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/node/base.rb:83:in `synchronize'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/node/element.rb:119:in `set'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/node/actions.rb:291:in `block in attach_file'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/node/actions.rb:334:in `while_visible'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/node/actions.rb:291:in `attach_file'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/session.rb:744:in `block (2 levels) in <class:Session>'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/capybara-3.15.1/lib/capybara/dsl.rb:51:in `block (2 levels) in <module:DSL>'
     # ./spec/spec_helpers/vendor_settings_helpers.rb:87:in `choose_csv_file'
     # ./spec/spec_helpers/vendor_settings_helpers.rb:91:in `upload_mocked_csv_file_data'
     # ./spec/selenium/settings/time_and_expense_settings/vendor_settings_spec.rb:71:in `block (4 levels) in <top (required)>'
     # ./spec/spec_helper.rb:176:in `block (3 levels) in <top (required)>'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/database_cleaner-1.7.0/lib/database_cleaner/generic/base.rb:16:in `cleaning'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/database_cleaner-1.7.0/lib/database_cleaner/base.rb:100:in `cleaning'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/database_cleaner-1.7.0/lib/database_cleaner/configuration.rb:86:in `block (2 levels) in cleaning'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/database_cleaner-1.7.0/lib/database_cleaner/configuration.rb:87:in `cleaning'
     # ./spec/spec_helper.rb:175:in `block (2 levels) in <top (required)>'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:123:in `block in run'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:110:in `loop'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:110:in `run'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/rspec-retry-0.6.1/lib/rspec_ext/rspec_ext.rb:12:in `run_with_retry'
     # /Users/juanca/.rvm/gems/ruby-2.3.3@mavenlink/gems/rspec-retry-0.6.1/lib/rspec/retry.rb:37:in `block (2 levels) in setup'

Finished in 1 minute 27.39 seconds (files took 17.57 seconds to load)
1 example, 1 failure

Steps to reproduce

require 'capybara/rails'

Capybara.register_driver :selenium do |app|
  profile = Selenium::WebDriver::Firefox::Profile.new

  browser_options = Selenium::WebDriver::Firefox::Options.new(profile: profile)

  driver_options = {
    options: browser_options,
    :browser => :remote,
    :url => 'http://localhost:4444/wd/hub',
  }

  Capybara::Selenium::Driver.new(app, driver_options)    
end
docker run   \
  --publish 4444:4444 \
  --publish 5900:5900 \
  --shm-size 2g \
  selenium/standalone-firefox-debug:latest

I could set us up with a test repository if we need more detailed steps to reproduce.

I was wondering whether this should be fixed with a simple conditional: if browser is remote then add the shim suggested by saucelabs.

juanca commented 5 years ago

I think I get where you are coming from. Here is perhaps another option: what about defaulting the file_detector when a browser: :remote configuration is used? Ideally, the default is only used when file_detector is not customized -- because otherwise this is broken. Or maybe it should be part of the Capybara documentation for remote file uploads?

However, I can also make this issue in the Ruby bindings repository for Selenium.

twalpole commented 5 years ago

Actually, assuming you're running current version of Firefox or Chrome - it should already be doing basically this - https://github.com/teamcapybara/capybara/blob/master/lib/capybara/selenium/node.rb#L306

I forgot I had implemented that -- and now I'm overthinking whether I should have implemented it :)

twalpole commented 5 years ago

Also rather than using make_visible, you might want to see if the block form of attach_file works for your use case - since it more closely imitates what a user would do

  attach_file Rails.root.join(filename) do
     # perform whatever action a user actually does that would open the file select dialog
  end
twalpole commented 5 years ago

Looks like I added it in Capybara 3.28

juanca commented 5 years ago

I'm going back and forth on whether it should be in Capybara, Selenium, or my code.

I am unfortunately stuck on the latest Capybara for Ruby 2.3. However, if it is on the latest then I can add yet another incentive for prioritizing Ruby 2.4.

I'll close this for now. I am going to leave the configuration in my test setup until I upgrade.

For those in my same boat:

require 'capybara/rails'

Capybara.register_driver :selenium do |app|
  profile = Selenium::WebDriver::Firefox::Profile.new

  browser_options = Selenium::WebDriver::Firefox::Options.new(profile: profile)

  driver_options = {
    options: browser_options,
    :browser => :remote,
    :url => 'http://localhost:4444/wd/hub',
  }

  driver = Capybara::Selenium::Driver.new(app, driver_options)    
  driver.browser.file_detector = lambda { |args| args.first.to_s } # Remove on latest Capybara
  driver
end

Update: The above does not work (for me). I am opting for customizing per attach_file usage for the sake of simplicity.

twalpole commented 5 years ago

@juanca you may want to focus on ruby 2.5/2.6 updating - 2.4 will be EOL relatively soon and Capybara will then drop support for it