3

Using selenium webdriver 3.12 and running a file upload tests on saucelabs(a selenium grid) as Win 10 chrome 66. I've tried to implement file detector and send keys into my code so that the system can take the file off my local machine and use it in saucelabs, but am running into the error:

Selenium::WebDriver::Error::ExpectedError:
       invalid argument: File not found : /Users/john.doe/Work/project/spec/support/apps/files/suggested_content/valid_sc.csv
         (Session info: chrome=66.0.3359.117)
         (Driver info: chromedriver=2.38.551601 (edb21f07fc70e9027c746edd3201443e011a61ed),platform=Windows NT 10.0.10586 x86_64)

The way I've implemented this is adding file detector as part of my spec helper, then requiring the spec helper at the top of my spec file, then using sendkeys to send the path for the file upload. Similar to Saucelabs best practices here

A snippet from inside my spec helper... I've instantiated the driver and added the code for file detector(this is slightly tweaked from sauce's and rubydoc's use since it is using current session driver instead of just driver):

       Capybara::Selenium::Driver.new(app,
                                       browser: :remote,
                                       url: url,
                                       desired_capabilities: capabilities)
        end

Capybara.current_session.driver.browser.file_detector = lambda do |args|
            str = args.first.to_s
            str if File.exist?(str)
          end

and what is getting called to upload the file:

    def add_file(file_name = 'valid_sc.csv')
      path = './spec/support/apps/files/suggested_content/'
      expose_file_upload
      page.find('input[id*=content--add-file')
          .send_keys(File.expand_path(path + file_name))
    end

The file is most certainly at that path and works just fine when I run the test locally. what am I missing here?

Doug
  • 53
  • 5

4 Answers4

2

This fixed it for me. I added the bottom portion of this snippet to my spec_helper.rb after registering the remote driver.

Capybara.register_driver :selenium do |app|
  # ...

  Capybara::Selenium::Driver.new(app,
                                 browser: :remote,
                                 url: url,
                                 desired_capabilities: capabilities)
end

# Add file upload capability on remote driver
page.driver.browser.file_detector = lambda do |args|
  str = args.first.to_s
  str if File.exist?(str)
end

Be sure to only do this when using a remote driver otherwise the regular, local selenium driver won't know how to handle the file_detector method.

Calaway
  • 714
  • 6
  • 12
  • That's exactly why you should put it inside the driver registration used for the remote driver as shown in my answer -- then it will only ever be used with the remote driver. – Thomas Walpole Aug 23 '18 at 22:47
1

Assuming the error message is coming as a response from the remote driver (stack trace would help confirm that) the fact the error message is including the original filename (rather than a temporary filename) tells you the file detector isn't being used. From your code it's not clear what Capybara.current_session is when your spec helper code is being run so a possible reason is that the current session isn't correct when you're adding the file detector. Try setting it in the driver registration block

Capybara.register_driver(...) do
  ...
  Capybara::Selenium::Driver.new(app,
                                   browser: :remote,
                                   url: url,
                                   desired_capabilities: capabilities).tap do |driver|
    driver.browser.file_detector = lambda do |args|
      str = args.first.to_s
      str if File.exist?(str)
    end
  end
end

There was also some work around file_detector in the selenium-webdriver 3.14 release so you may want to try upgrading too.

On a separate note have you tried using Capybaras attach_file with the make_visible option rather than rolling your own expose_file_upload ?

page.attach_file(File.expand_path(path + file_name), make_visible: true)
Thomas Walpole
  • 48,548
  • 5
  • 64
  • 78
1

Testing file upload using capybara on saucelabs is little bit tricky. I have faced similar kind of issue some time back.

try without file-detector and use capybara's attach_file method.

Overview:

while running file upload if you are using attach_file capybara function this will validate the file existence path on your local machine, see below attach_file method source code from rubydocs

    def attach_file(locator = nil, path, make_visible: nil, **options)
      Array(path).each do |p|
        raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
      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 = find(:file_field, locator, options.merge(visible: :all))
        while_visible(ff, make_visible) { |el| el.set(path) }
      else
        find(:file_field, locator, options).set(path)
      end
    end

but while execution it searches the same path on saucelabs machine, which can give error if file is not present on saucelabs machine.

so for simple solution, you can create a custom attach_file method without path validation like below:

    def attach_file_new(locator, path, options={})
      find(:file_field, locator, options).set(path)
    end

element visibility you can check and set through executing javascript or jquery methods and provide the actual saucelabs machine's file path to this method.

so what it does it will not validate path on your local machine rather directly picks file from saucelabs machine.

some more info: while executing tests on saucelabs it creates machine at the time of execution so you can't put your file on that machine before.

so better approach would be to add some setup steps before actual file upload steps in which you open the browser and hit a direct download file url e.g some dropbox file url or google drive direct file download url, which will download file for you on saucelabs machine and download would always be in download directory so you can provide the path of that directory and filename to attach_file method.

Hope this will help you.

Chandella07
  • 2,089
  • 14
  • 22
  • 1
    The whole point of `file_detector` is that you shouldn't need to workaround getting the file onto the remote machine - Selenium should handle the uploading from local to remote for you. – Thomas Walpole Aug 23 '18 at 21:33
  • Thanks for the information @ThomasWalpole got your point. New learning for me but the above solution did work for me. – Chandella07 Aug 24 '18 at 03:56
0

I stumbled upon this thread for another error, but found the context on this question useful. Adding it here in case anyone's stuck on this:

NoMethodError:
           undefined method `file_detector=' for #<Selenium::WebDriver::Chrome::Driver:0x00007ff8edf60020>

My version setup looked like:

  • ChromeDriver 83.0.4103.39
  • selenium-webdriver 3.14

To fix this:

  1. I followed Thomas's recommendation to put driver.browser.file_detector inside the driver registration used for the remote driver as shown in his answer
  2. I upgraded selenium-webdriver gem from 3.14 to 3.142.7
harshp
  • 101
  • 1
  • 6