1
votes

Weird problem.

I'm trying to test sign in, using Capybara and RSpec, but it seems that Capybara is filling in the wrong fields, so that the user getting signed in is not being authenticated.

I created the test user using FactoryGirl:

#factories.rb

FactoryGirl.define do

    #...

    factory :user do
        name "guest"
        password "pwordtest"
        password_confirmation "pwordtest"
    end

end

Then I wrote the following test (with a lot of "puts" stuff in it for debugging this problem):

#posts_spec.rb

describe "Valid post submission" do

 it "should log in a user and let him make a post" do
  User.destroy_all
  visit '/access' #My Login page
  user = FactoryGirl.create(:user)
  puts "name"
  puts user.name
  fill_in :name, with: "guest" # I did these literally to make sure FactoryGirl wasn't the problem.
  fill_in :password, with: "pwordtest"
  click_on "Log In"
  assert User.count == 1
  puts "authing test"
  current_path.should eq(new_post_path)
 end
#...
end

In my sessions controller (more debuggery):

def create
    user = User.find_by_name(params[:name])
    puts "all"
    puts params
    puts "pre-inspect"
    puts user.inspect
    if user && user.authenticate(params[:password])
        session[:user_id] = user.id
      puts "In IF"
        redirect_to new_post_path
    else
        flash.now[:error] = "Invalid password/username combo."
      puts "there"
        render 'new' 
    end
  end

When I try to run the test, I get the following error:

name
guest
all
{"utf8"=>"✓", "name"=>"pword", "password"=>"", "commit"=>"Log In", "action"=>"create", "controller"=>"sessions"}
pre-inspect
nil
there
authing test
F

Failures:

  1) Posts Valid post submission should log in a user and let him make a post
     Failure/Error: current_path.should eq(new_post_path)

       expected: "/posts/new"
            got: "/sessions"

       (compared using ==)
     # ./spec/requests/posts_spec.rb:28:in `block (3 levels) in <top (required)>'

In other words, my debugging prompts suggest that the FactoryGirl user is fine, but somehow, Capybara is failing to assign those values to the right fields, so that params (after "all" in the debug prints) gets assigned "pword" as a name and "" as a password, instead of "guest" as name and "pword" as password. As a consequence, the test user isn't authenticated, and the session isn't started. This is particularly weird, cause the fields are definitely named correctly:

#sessions/new.html.erb

<div class="center_login">
    <h1>Log In</h1>
    <%= form_tag sessions_path do %>
      <div class="field">
        <%= label_tag :name %>
        <%= text_field_tag :name, params[:name] %><br />
      </div>
      <div class="field">
        <%= label_tag :password %>
        <%= password_field_tag :password %><br />
      </div><br>
      <div class="actions"><%= submit_tag "Log In", class: "btn" %></div>
    <% end %>
</div>

Any idea what's going on? Two other people and I messed around with this for an hour + and just can't figure it out.

EDIT -- It should be mentioned that when I navigate through the site as an actual person, this all works perfectly. As in, the username I put in the name field and the password I put in the password field correspond to those in the database, and I'm signed in just fine. Thus my suspicion that some Capybara failure to fill in the right thing is at fault here.

EDIT 2 -- Test.log output of one test below, per request:

Connecting to database specified by database.yml
  [1m[36m (0.4ms)[0m  [1mbegin transaction[0m
Started GET "/posts/new" for 127.0.0.1 at 2013-02-14 07:29:55 -0800
Processing by PostsController#new as HTML
Redirected to http://www.example.com/
Filter chain halted as :authorize rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.0ms)
Started GET "/" for 127.0.0.1 at 2013-02-14 07:29:55 -0800
Processing by StaticsController#resume as HTML
  Rendered statics/_stars.html.erb (1.0ms)
  Rendered statics/_stars.html.erb (0.4ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_stars.html.erb (0.3ms)
  Rendered statics/_skills.html.erb (14.9ms)
  Rendered statics/_collapse_start.html.erb (0.6ms)
  Rendered statics/_collapse_start.html.erb (0.1ms)
  Rendered statics/_collapse_start.html.erb (0.1ms)
  Rendered statics/_collapse_start.html.erb (0.1ms)
  Rendered statics/_collapse_start.html.erb (0.1ms)
  Rendered statics/resume.html.erb within layouts/application (68.4ms)
  Rendered layouts/_shim.html.erb (0.2ms)
  Rendered layouts/_header.html.erb (1.0ms)
Completed 200 OK in 163ms (Views: 162.3ms | ActiveRecord: 0.0ms)
  [1m[35mUser Load (12.1ms)[0m  SELECT "users".* FROM "users" 
Started GET "/access" for 127.0.0.1 at 2013-02-14 07:29:55 -0800
Processing by SessionsController#new as HTML
  Rendered sessions/new.html.erb within layouts/application (1.2ms)
  Rendered layouts/_shim.html.erb (0.0ms)
  Rendered layouts/_header.html.erb (0.5ms)
Completed 200 OK in 32ms (Views: 32.1ms | ActiveRecord: 0.0ms)
  [1m[36m (0.1ms)[0m  [1mSAVEPOINT active_record_1[0m
  [1m[35mUser Exists (0.1ms)[0m  SELECT 1 AS one FROM "users" WHERE LOWER("users"."name") = LOWER('guest') LIMIT 1
Binary data inserted for `string` type on column `password_digest`
  [1m[36mSQL (30.0ms)[0m  [1mINSERT INTO "users" ("created_at", "name", "password_digest", "updated_at") VALUES (?, ?, ?, ?)[0m  [["created_at", Thu, 14 Feb 2013 15:29:55 UTC +00:00], ["name", "guest"], ["password_digest", "$2a$10$Y9NfnYjForrfufZOaqgQj.BdcHYLh.tkYomCVfHiJ4McbWMem445e"], ["updated_at", Thu, 14 Feb 2013 15:29:55 UTC +00:00]]
  [1m[35m (0.1ms)[0m  RELEASE SAVEPOINT active_record_1
Started POST "/sessions" for 127.0.0.1 at 2013-02-14 07:29:55 -0800
Processing by SessionsController#create as HTML
  Parameters: {"utf8"=>"✓", "name"=>"pword", "password"=>"[FILTERED]", "commit"=>"Log In"}
  [1m[36mUser Load (0.1ms)[0m  [1mSELECT "users".* FROM "users" WHERE "users"."name" = 'pword' LIMIT 1[0m
  Rendered sessions/new.html.erb within layouts/application (0.7ms)
  Rendered layouts/_shim.html.erb (0.0ms)
  Rendered layouts/_header.html.erb (0.5ms)
Completed 200 OK in 21ms (Views: 2.7ms | ActiveRecord: 0.1ms)
  [1m[35m (0.1ms)[0m  SELECT COUNT(*) FROM "users" 
  [1m[36m (0.5ms)[0m  [1mrollback transaction[0m

As you can see, the "parameters" passed to the sessions controller 6ish lines from the bottom include the name "pword" (and a filtered and possibly empty password), which is weird/frustrating.

EDIT 3 -- Using the most up-to-date version of Capybara, in my test group. And as of this new posting, I have run into an essentially identical problem in a spec for another controller. That is, I'll tell Capybara to fill in field 1 with "A", 2 with "B" and 3 with "C", and the params from that controller will show 1 filled with "B", 2 filled with "C" and 3 empty, as if Capybara is filling out each field prior to the one I tell it to or something.

EDIT 4 -- Following Dave S's suggestion, I took a screenshot of the sign in process (and also of the posting process, which, as stated in edit 2, has the same error).

Two different things are happening! In the login page, it looks like (because the password is blocked out), it's actually filling in the correct info (name == guest, and password == five stars, which is the same number of characters as password (and guest)), but if I save a page instead of a screenshot and click through, it goes to sessions and errors, just like the tester.

In my saved page/screenshot of the posting page, Capybara is definitely filling in the wrong fields. The name field is filled with what I said should be content, and the content field is empty. I think this is because I created new unique IDs for login but not for posting, and I'll see what happens if I do that for both. I'll also change the password to something differentiable from "guest" when blocked out, then report back.

EDIT 5 -- The new screenshots:

enter image description here

enter image description here

And the code of the two views, to give a sense of what might be different here. (I also changed the above factory code to make sense out of the (correct) password length in the screenshot.

# new posts _form.html.erb
<%= form_for(@post) do |f| %>
  <% if @post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% @post.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name, id: "post_name" %>
  </div>
  <div class="field text-area">
    <%= f.label :content %><br />
    <%= f.text_area(:content, :size => '50x20', id: "post_content") %>
  </div>

    <div class="actions btn-group">
      <%= f.submit 'Post It', class: "btn" %>
    </div>

<% end %>

And login:

# login (new.html.erb in sessions)

<div class="center_login">
    <h1>Log In</h1>
    <%= form_tag sessions_path do %>
      <div class="field">
        <%= label_tag :name %>
        <%= text_field_tag :name, params[:name], id: "sessions_name" %><br /> 
        # The above name change seemed to possibly fix it. The weird thing, though, is that
        # a similar change in the posts form above did NOT fix that problem.
      </div>
      <div class="field">
        <%= label_tag :password %>
        <%= password_field_tag :password %><br />
      </div><br>
      <div class="actions"><%= submit_tag "Log In", class: "btn" %></div>
    <% end %>
</div>

Now (with name in login called seesions_name), login works 100%. The params are right, etc. But posting doesn't, because (despite my having made essentially the same change) Capybara is still filling in the wrong fields. Does any of this make the issue any clearer? I'm stumped.

EDIT 6 -- the full repo can be found here

2
What do you see in your Rails log (test.log), what gets posted? Are you sure the field ids are actually 'name' and 'password'? What happens when you give those two fields in new.html.erb hardcoded ids?Bitterzoet
Yeah. Actually. I forgot to post that, but that's what led to my theory originally. I'll post the entire log associated with one run through that test.Sasha
So good suggestion -- adding the ids sessions_name and sessions_password to the fields and referring to those ids in capybara fixed the problem (If I could award your comment as the correct answer, I would), but I'd still love to know WHY this was going on.Sasha
Are you sure you don't have other inputs on your page that might have been selected/used by Capybara?Bitterzoet
One more thought, is there possibly another element in the DOM with name/id "password"? Id collisions will make capybara all sorts of unhappy (rightfully so).Dave S.

2 Answers

0
votes

I was just having a similar problem with testing my app's login functionality: it worked fine when I logged in manually, but login failed when testing it using Capybara.

You seem to be having some problems I didn't have, namely, Capybara filling in fields incorrectly. I'm not sure what that's due to, but you might be having more than one problem. After debugging, I found that my problem was due to a buggy user factory.

What I had was:

require 'digest/sha1'

FactoryGirl.define do
  factory :user do
    login      'hitchcock'
    fname      'Alfred'
    lname      'Hitchcock'
    email      '[email protected]'
    password   Digest::SHA1.new << 'MacGuffin'
  end
end

The field that sets the password was incorrect. This is the correct version:

    password   (Digest::SHA1.new << 'MacGuffin').to_s

The "password" field of the class User actually contains a password digest, not a password, but I'm working with a legacy database. In any case, I had calculated the digest of the password incorrectly, forgetting the to_s method, which caused my test cases' login attempts using username 'hitchcock' and password 'MacGuffin' to fail.

Now, looking at your factory, it seems to me you might have a similar problem. You migration file 20130206234907_create_users.rb looks like this:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :password_digest
      t.timestamps
    end
  end
end

which shows you're storing the digest of the password, not the password itself, which is right and proper. However, your user factory, in file factories.rb, is:

factory :user do
  name "guest"
  password "pwordtest"
  password_confirmation "pwordtest"
end

Your factory doesn't specify a password_digest field for the user it creates, so when in your posts_spec.rb you create a user by commanding

user = FactoryGirl.create(:user)

I'm wondering if a user is being created in the database at all, or if it is, what its password_digest field is going to be? Remember, when running tests, the contents of the test database are first cleared, so if your testing code doesn't create a valid user in the database, your login test won't work.

0
votes

I had exactly this problem: Wrong fields being filled when using fill_in.

Solution was to follow the docs and use strings instead of symbols for the matchers:

I replaces this:

fill_in :email, with: email
fill_in :password, with: password

With:

fill_in "Email", with: email
fill_in "Password", with: password

And then it worked as expected

Docs: https://github.com/jnicklas/capybara#using-capybara-with-rspec