New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fill_in sometimes does not finish filling in a field #1890

Closed
kiramclean opened this Issue Jul 13, 2017 · 14 comments

Comments

Projects
None yet
10 participants
@kiramclean

kiramclean commented Jul 13, 2017

Meta

Capybara Version:
2.13.0
Driver Information (and browser if relevant):
selenium-webdriver (3.4.3) with chrome Version 59.0.3071.115

Expected Behavior

Using fill_in finishes filling in the field before moving on.

Actual Behavior

Sometimes when filling in a text field with fill_in, the desired content is not done filling in before capybara moves on, causing subsequent expectations to fail.

Steps to reproduce

  • Fill in a text field with capybara
  • Have an expectation later that checks for the content of that field
  • Sometimes (not always), the expectation will fail because the field was only partially filled in

Example:

    fill_in 'my_text_field', with: 'This is a test'
    click_on 'Submit'
    expect(page).to have_content 'This is a test'

=> Failure/Error: expect(page).to have_content 'This is a test'
       expected to find text "This is a test" in "This is a te"
@twalpole

This comment has been minimized.

Collaborator

twalpole commented Jul 13, 2017

A couple of people have reported this recently. In the past it was usually caused by behaviors on the page implemented with badly written JS that would miss keystrokes if they were sent too fast. It could still be that (what JS behaviors are on your page) or it could be an issue in selenium-webdriver/chromedriver (maybe one of them doesn't wait for the keystrokes to be sent before returning - a sleep after fill_in fixing it would tend to indicate that). Without a reproducible test case we have no way of debugging this. Therefore if you can produce a test case that will exhibit this failure at least some of the time we can attempt to figure it out, otherwise there's not much we can do.

@twalpole twalpole added the Need Info label Jul 13, 2017

@teamcapybara teamcapybara deleted a comment from workgena Jul 17, 2017

@kiramclean

This comment has been minimized.

kiramclean commented Jul 18, 2017

Thanks for the quick reply. I'll work on isolating a reproducible test case. Definitely sounds like the bug report above.

@SzNagyMisu

This comment has been minimized.

SzNagyMisu commented Jul 27, 2017

Bumped into the same problem, only with #set (which is called internally by #fill_in).
I am testing a JS-heavy application with rspec-rails + capybara. We have input fields hidden behind divs containing their value in a formatted way. One has to click on the div to make the input appear and fill it.

6 times out of 10 the spec works as expected, the div is clicked and #set fills the appearing input correctly. In the other 4 cases, however, I only get the beginning.
Since some milliseconds of sleep before #set seemed to solve the problem I concluded that the problem must be the race condition between setting the field visible and filling in the value. In other words: the #set method starts to fill in the input, then it becomes visible as an effect of the click and #set stops its work, because the object is no longer the the same it started to fill in. In the other 6 cases the input becomes visible before #set starts to work.

Is this a possible explanation?

@twalpole

This comment has been minimized.

Collaborator

twalpole commented Jul 27, 2017

@SzNagyMisu As you stated #fill_in is basically just implemented as find_field(...).set so the error would be in either. Visibility shouldn't be an issue, because fill_in and however you locate the element to call set on it wait for the element to be visible before returning it (unless you're doing something unwise like passing visible: false/:all/:hidden). This is either a bug in chrome_driver or you have some other badly behaving JS behavior attached to the field that is triggering when the field becomes visible.

@SzNagyMisu

This comment has been minimized.

SzNagyMisu commented Jul 27, 2017

Yes, you are right, I query the field with find('input', visible: :all) (because I had some problem with its visibility in the tests - but that is off topic). So, it should explain...

Thanks for the quick reply!

@twalpole

This comment has been minimized.

Collaborator

twalpole commented Aug 4, 2017

Closing - If a reproducible test case is provided that shows this is caused by Capybara and not Chromedriver we can reopen

@twalpole twalpole closed this Aug 4, 2017

@lacco

This comment has been minimized.

lacco commented Nov 29, 2017

After switching from poltergeist/phantomjs to headless chrome, I had similar issues which were related to CSS3 animations with Bootstrap modals. The final solution was to remove the fade class for those modals, other ways of disabling animations in tests didn't work for me.

@eric-simonton-sama

This comment has been minimized.

eric-simonton-sama commented Dec 14, 2017

Thanks @lacco - that workaround is working for us!

@fschwahn

This comment has been minimized.

fschwahn commented Dec 21, 2017

I also ran into this issue, and was able to come up with a simple repro-script. The culprit is a bootstrap modal, so the problem seems to be caused by animated elements. This script fails always for me:

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "capybara"
  gem "selenium-webdriver"
end

require "capybara"

html = DATA.read
app = proc { |env| [200, { "Content-Type" => "text/html" }, [html] ] }

INPUT = ("STRING" * 100)

sess = Capybara::Session.new(:selenium_chrome_headless, app)
sess.visit("/")
sess.click_button "Launch demo modal"
sess.fill_in "test-input", with: INPUT
value = sess.find("#test-input").value
raise "Input error: #{value} not equal input" if value != INPUT

__END__

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <!-- Button trigger modal -->
    <button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
      Launch demo modal
    </button>

    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            <h4 class="modal-title" id="myModalLabel">Modal title</h4>
          </div>
          <div class="modal-body">
            <textarea id="test-input"></textarea>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
  </body>
</html>

I guess the reason why this comes up more often now is that headless chrome is more susceptible to this than phantomjs (this test passes when poltergeist is used as a driver).

I was able to get this to pass by doing the following:

jQuery(function() {
  $.support.transition = false;
})

This also makes the problems go away in my test suite.

So in conclusion: this most probably has nothing to do with capybara.

@choosen

This comment has been minimized.

choosen commented Dec 22, 2017

$.fx.off = true

Can be also helpfull for disable jQuery animations
https://api.jquery.com/jquery.fx.off/

@srghma

This comment has been minimized.

srghma commented Jan 2, 2018

i'm using send_keys

module Macroses
  module Auth
    module_function

    def native_fill_field(selector, text)
      text.split('').each { |c| find_field(selector).native.send_keys(c) }
    end

    def login(user)
      visit root_path
      find('.nav-right', text: 'LOGIN').click

      within '#new_user' do
        native_fill_field 'user[password]', user.password
        native_fill_field 'user[email]',    user.email
      end

      click_link_or_button 'LOGIN'

      expect(page).to have_current_path root_path
      expect(page).to have_content('Signed in successfully')
    end
  end
end

myabc added a commit to rubyberlin/cfp-app that referenced this issue Apr 30, 2018

Remove event handler for autofocus on modal shown
This resolves a timing issue affecting the Event Config feature.

Although the Bootstrap documentation advises adding this snippet, the
docs do not account for the fact that we are loading our form via XHR
and inserting it into the DOM. When creating a form dynamically in
Chrome 66, the `autofocus` attribute does apply focus correctly.

What's more, the presence of this handler actually causes a race
condition with Capybara - calling `fill_in` immediately after the
opening of the modal fails more often than not: when the first field to
filled has the `autofocus` attribute then the cursor is prone to being
repositioned in the midst of WebDriver sending keystrokes; when the
first field does not have `autofocus`, it sporadically loses its focus
back to its sibling `autofocus` field.

Similar, but not necessarily related:

- teamcapybara/capybara#1890
- erikras/redux-form#686

myabc added a commit to rubyberlin/cfp-app that referenced this issue Apr 30, 2018

Remove event handler for autofocus on modal shown
This resolves a timing issue affecting the Event Config feature.

Although the Bootstrap documentation advises adding this snippet, the
docs do not account for the fact that we are loading our form via XHR
and inserting it into the DOM. When creating a form dynamically in
Chrome 66, the `autofocus` attribute does apply focus correctly.

What's more, the presence of this handler actually causes a race
condition with Capybara - calling `fill_in` immediately after the
opening of the modal fails more often than not: when the first field to
filled has the `autofocus` attribute then the cursor is prone to being
repositioned in the midst of WebDriver sending keystrokes; when the
first field does not have `autofocus`, it sporadically loses its focus
back to its sibling `autofocus` field.

Similar, but not necessarily related:

- teamcapybara/capybara#1890
- erikras/redux-form#686
@seanlinsley

This comment has been minimized.

seanlinsley commented May 16, 2018

I tried the solutions listed here, and others like checking for any elements matching jQuery's :animated selector, but the only thing that consistently worked was to remove fade like @lacco suggested.

@panasyuk

This comment has been minimized.

panasyuk commented Jun 5, 2018

Another approach was to disable animations/transitions/transformations in CSS. But there still were ~ 30% probability of errors when we have been using Bootstrap modals with fade class. Removing fade class in test environment solved the problem. Thanks @lacco!

GUI added a commit to NREL/api-umbrella that referenced this issue Nov 12, 2018

Disable bootstrap modal fades to fix integration tests.
Our CSS approach to disable animations doesn't seem sufficient for some
reason with headless chrome. So go ahead and disable the fades
completely, which seems to fix this. Others reporting the same behavior:
teamcapybara/capybara#1890 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment