Closed kiramclean closed 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.
Thanks for the quick reply. I'll work on isolating a reproducible test case. Definitely sounds like the bug report above.
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?
@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.
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!
Closing - If a reproducible test case is provided that shows this is caused by Capybara and not Chromedriver we can reopen
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.
Thanks @lacco - that workaround is working for us!
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">×</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.
$.fx.off = true
Can be also helpfull for disable jQuery animations https://api.jquery.com/jquery.fx.off/
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
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.
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!
try this: input = find(:id, "inputId") input.click.send_keys "This is a test"
Locking this because random posting of code fragments without context really isn't helping anyone
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: