Search code examples
ruby-on-railsrubyselenium-chromedrivercapybararspec-rails

Capybara don't replace default value of input


I have a pretty simple user scaffold pages and some tests on them.

it 'update' do
      visit '/users/new'
      fill_in 'user[login]', with: 'login'
      fill_in 'user[password]', with: 'password'
      click_on 'Create User'
      expect do
        visit '/users/1/edit'
        fill_in 'user_login', with: 'login'
        fill_in 'user_password', with: 'password256'
        click_on 'Update User'
        # p User.all
      end.to change { User.find_by(login: 'login').password }
    end

In 10% probability (I'm also inclined to speculate that this might be due to the chrome window losing focus) I've got an error:

2) Users user update
     Failure/Error:
       expect do
         visit '/users/1/edit'
         fill_in 'user_login', with: 'login'
         fill_in 'user_password', with: 'password256'
         click_on 'Update User'
         # p User.all
       end.to change { User.find_by(login: 'login').password }
     
     NoMethodError:
       undefined method `password' for nil:NilClass
     
             end.to change { User.find_by(login: 'login').password }
                                                         ^^^^^^^^^

And screenshot by Capybara Capybara screenshot With message from console by p Users.all

#<ActiveRecord::Relation [#<User id: 1, login: "loginlogin", password: [FILTERED], created_at: "2022-12-24 18:36:57.938067000 +0000", updated_at: "2022-12-24 18:36:58.270584000 +0000">]>

Source form is

<%= form_with(model: user) do |form| %>
  <% if user.errors.any? %>
    <div style="color: red">
      <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
        <% user.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= form.label :login, style: "display: block" %>
    <%= form.text_field :login %>
  </div>

  <div>
    <%= form.label :password, style: "display: block" %>
    <%= form.password_field :password %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>

Edit user form screenshot

I've already tried to add sleep above and below every fill_in command, but it hadn't helped. Also i've tried to add

fill_options: { clear: true }

as fill_in parameter.


Solution

  • The doubling up of form contents is generally caused by a bug in the chrome driver. One workaround when you experience it is to specify fill_options: { clear: :backspace } to fill_in. Another likely issue here is that you're not letting the user get created sometimes. click_on 'Create User' returns immediately after clicking on the button (or link) and doesn't wait for any behavior that click triggers. Because of this it's possible that immediately calling visit '/users/1/edit' after it will end up cancelling the form submission you're using to create the user in the first place. Best practice is to expect on whatever change would occur in the page after the user is created before moving on to visit the new url. So something like

    ...
    fill_in 'user[login]', with: 'login', fill_options: { clear: :backspace }
    fill_in 'user[password]', with: 'password', fill_options: { clear: :backspace }
    click_on 'Create User'
    expect(page).to have_text('User created')
    expect do
      visit '/users/1/edit'
      fill_in 'user_login', with: 'login'
      ...
    

    Another thing to note is that unless this is always the first test run it's highly unlikely for the user to have an id of 1 so hardcoding urls like that isn't really going to work well as your test suite grows.