Chromedriver not filling all the whole password field in automated RSpec, Capybara, Feature Tests 

This is such a lovely story. You will like it.

When using google chromedriver to run capybara tests sometimes, just sometimes, especially if the tests are run in parallel, when the test has to fill a text field like a password, it fills only part of it. Last time checked for chromedriver 2.45

TL; DR;

Solution – google does not care, or it seems it is too difficult for the chromedriver team to resolve so there simply is no solution.

What is the test?

We are using Rails, Rspec, Capybara, Google chromedriver. We are developing feature tests. Tests are run in parallel with

rake parallel:spec

Here is the test in question. Simply fill a password on the form for upgrading a subscription, click on Confirm and expect to be redirected to a page that says – “You’ve upgraded your subscription”


def submit_and_expect_success password
      # dialog opens to confirm with password
      fill_in "owner_password", with: password
      click_on "Confirm"

      expect_redirect_to "/subscriptions/#{subscription.to_param}"

      # If it redirects to this page, it means that the change was successful
      expect(page).to have_current_path "/subscriptions/#{subscription.to_param}"
end

And the tests are failing with timeout at expect_redirect_to. No, expect_redirect_to is a custom method, because we are using ActionCable to wait for subscription upgrade to finish. Because of the payment service at the back this sometimes takes a while and we want to show a nice progress and we need a websocket. But that being said the method is nothing special.

module ExpectRedirect
  def expect_redirect_to url
    # If the path doesn't change before the timeout passes,
    # the test will fail, because there will be no redirect

    puts "expect url: #{url}"
    begin
      Timeout.timeout(Capybara.default_max_wait_time) do
        sleep(0.1) until url == URI(page.current_url).path
        page.current_url
      end
    rescue Timeout::Error=>e
      puts "Current url is still: #{page.current_url}"
      puts page.body
      raise e
    end
  end
end

If we are redirected to the url withing Capybara.default_max_wait_time than everything is fine. If not, we are raising the Timeout::Error.

Parallel execution

For some reason the test in question fails only when we are doing a parallel execution. Or at least mostly when we are doing parallel execution of the tests. So we moved through some nice articles to revise our understanding of Timeout again and again.

https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/

But nevertheless the tests were failing with Timeout::Error on waiting for a redirect and in the html we could see the error returned by the server:

<div><p>Invalid password</p></div>

How come the password is Invalid

No this took a while to debug and it seems rather mysterious but this is what we got:

User password in DB is: 10124ddeaf1a69e3748e308508d916b6

The server receives from the html form: 10124ddeaf1a69e3748e30850

User password in DB is: 74c2a3e926420e1a30363423f121fc1e

The server receives from the html from: 74c2a3e926420e1a3

and so on and so on.

Sometimes the difference is 8 symbols. Sometimes it is 2. Sometimes it is 16.

It seems to be a client side issue

Like. JavaScript. If there is an error this strange it has to be in the JavaScript. Right. There in the javascript we see:

let form_data = form.serializeObject();
this.perform('start_change', form_data);

The form gets serialized. Probably it is not serialized correctly. Probably the values that we are sending are just not the values on the form. So I revised my knowledge on serializing objects in JavaScript with

https://stackoverflow.com/questions/17488660/difference-between-serialize-and-serializeobject-jquery

So far so good. But the serialization was not the problem. Here is what I did. I fixed all the passwords to be 32 symbols.

let form_data = form.serializeObject();
 if(form_data["owner_password"].lenght != 32) {
        form_data["owner_password"] = "this was:" + form_data["owner_password"] + " which is less than 32 symbols"
      }
this.perform('start_change', form_data);

It it happened. The value of the password field was simply not 32 symbols long. It was not filled during the test.

A little bit of search and we arrive at:

https://github.com/teamcapybara/capybara/issues/1890

and there in the bottom of the issue, there is the standard: “Not our problem resolution” with the link to:

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

It seems that google chromedriver is not filling all the characters in the password field. It is doing it on random and is completely unpredictable.

Issue still exists on:

Issue still exist for

Chrome: Version 71.0.3578.98 (Official Build) (64-bit)
Chromedriver chromedriver --version
ChromeDriver 2.45.615279 (12b89733300bd268cff3b78fc76cb8f3a7cc44e5)
Linux kireto-laptop3 4.4.0-141-generic #167-Ubuntu x86_64 GNU/Linux
Description:	Ubuntu 16.04.5 LTS
Release:	16.04
Codename:	xenial

Today we would try Firefox driver.