teamcapybara / capybara

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

fill_in sometimes does not finish filling in a field #1890

Closed kiramclean closed 7 years ago

kiramclean commented 7 years ago

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

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 commented 7 years ago

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 commented 7 years ago

Possibly https://bugs.chromium.org/p/chromedriver/issues/detail?id=1771&q=sendkeys&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary

kiramclean commented 7 years ago

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

SzNagyMisu commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

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

lacco commented 6 years ago

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 commented 6 years ago

Thanks @lacco - that workaround is working for us!

fschwahn commented 6 years ago

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 commented 6 years ago
$.fx.off = true

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

srghma commented 6 years ago

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
seanlinsley commented 6 years ago

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 commented 6 years ago

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!

Dioven15 commented 5 years ago

try this: input = find(:id, "inputId") input.click.send_keys "This is a test"

twalpole commented 5 years ago

Locking this because random posting of code fragments without context really isn't helping anyone