1
votes

I'm testing file upload functionality on a page built with React. The React page has a hidden file input field with an onChange event listener wired up to it. When a file is selected it triggers the onChange and the file is processed. I have a test that uses Capybara and javascript to expose the hidden input, use Capybara's attach_file method to attach the file to the input and check to make sure the file uploaded and appears in the user's file list. This test works in Chrome and the file is processed after using Capybara's attach_file method, but it does not work in Firefox - the file goes into the input, but it is never processed. I suspect that for some reason React isn't detecting the onChange in Firefox or it isn't getting fired. I need the test to work in both browsers.

I've tried using execute_script with the appropriate vanilla javascript (no jquery) to create and dispatch both change and input events in firefox with no luck. After everything I try, I end up with nothing more than the exposed input field and the file name selected showing in it. As a SDET, I must work with the front-end code as-is.

React input for the file

<input
          className={css.hiddenInput}
          onChange={event => {
            actions.uploadAttachment(event, personOneId, personTwoId);
          }}
          ref={file => (this.file = file)}
          type="file"
        />

Ruby/Capybara/javascript code exposing the input and attaching the file

def expose_attachment_input
      page.execute_script(
        'document.querySelector(`input[class^=attachments__hidden-input]`)
        .setAttribute(`style`, `visibility: visible; width: auto; height: auto;`);'
      )
      page.execute_script(
        'document.querySelector(`input[class^=attachments__hidden-input]`)
        .setAttribute(`name`, `attachments`);'
      )
    end

def add_new_attachment(attachment = 'some.gif')
      expose_attachment_input
      attach_file(
        'attachments',
        File.expand_path("./spec/files/#{attachment}")
      )
    end

And a few examples of things I tried to trigger the event after the attach_file has been completed

page.execute_script("document.getElementsByName('attachments')[0].dispatchEvent(new Event('change'))")

page.execute_script("document.getElementsByName('attachments')[0].dispatchEvent(new Event('input'))")

page.execute_script("document.getElementsByName('attachments')[0].dispatchEvent(new Event('change'), { bubbles: true })")

Per @Thomas Walpole's suggestions, I also tried the following things and received the following errors

page.attach_file(File.expand_path("./spec/files/some.gif"), make_visible: true)
Capybara::ExpectationNotMet: The style changes in :make_visible did not make the file input visible

attach_file(File.expand_path("./spec/files/some.gif”), make_visible: {visibility: 'visible', width: 'auto', height: 'auto'})
The hidden input temporarily flashes visible, then disappears and no file attachment is processed.


attach_file(File.expand_path("./spec/files/some.gif")) do
   page.find('span', text: ‘Add Attachments’).click
 end  
Capybara::ElementNotFound: Unable to find visible file field nil with allow_self true

attach_file(File.expand_path("./spec/files/some.gif”), make_visible: true) do
   page.find('span', text: ‘Add Attachments’).click
 end  
Capybara::ExpectationNotMet: The style changes in :make_visible did not make the file input visible
1
What version of Capybara are you using?Thomas Walpole
and what versions of geckodriver, selenium-webdriver, and firefox?Thomas Walpole

1 Answers

0
votes

The fact that it works in Chrome but not in Firefox would tend to indicate a bug in either Capybara, geckodriver, or your app, so make sure you're running current versions of Capybara and geckodriver. If that doesn't fix things you can try clicking on another element after attaching the file to see if that triggers blur/change events (different browsers may generate the event at different times).

Beyond that, current versions of Capybara provide support for making hidden file inputs visible without you having to resort to custom JS, which may be more reliable and have more consistent behavior. In your case that would be something like (no locator necessary if only one file input on page)

attach_file(File.expand_path("./spec/files/#{attachment}"), make_visible: true)

If the default CSS applied doesn't make the file input visible you can specify custom CSS like

attach_file(File.expand_path("./spec/files/#{attachment}"), 
            make_visible: {visibility: 'visible', width: 'auto', height: 'auto'})

A cleaner, and more user like, solution in very recent versions of Capybara would be to use attach_file in its block mode which requires you to perform the actions a user would use to trigger file selection and then attempts to determine which file input is being used without worrying about visibility.

attach_file(File.expand_path("./spec/files/#{attachment}")) do
  click_button('Choose files') # whatever action a user does to trigger file selection
end

Note: using execute_script to manually generate and dispatch events is generally a bad idea when testing since it allows to do things a user never could and may hide real bugs in your app.