1
votes

I'm working through Hartl's well known Ruby on Rails 4 tutorial. I'm not new to Rails, but I am new to testing: RSpec, Capybara, FactoryGirl, etc. I'm specifically having a issue with the tests for editing a user found in chapter 9 (http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-users#sec-successful_edits). I'll post what I've tried to remedy first and then the relevant code:

  1. Triple checked my code compared to the tutorial. Everything looks ok, but I may be missing something.
  2. Restarted my server and reset, re-populated, and re-setup the test db.
  3. Checked the users in the test and development SQLite dbs using SQLite Manager extension in FF.

Here are the errors:

1) Authentication Signin page with valid information followed by signout 
 Failure/Error: before { click_link "Sign Out" }
 Capybara::ElementNotFound:
   Unable to find link "Sign Out"
 # ./spec/requests/authentication_pages_spec.rb:41:in `block (5 levels) in <top (required)>'

 2) UserPages edit with invalid information 
 Failure/Error: before { click_button "Save changes" }
 Capybara::ElementNotFound:
   Unable to find button "Save changes"
 # ./spec/requests/user_pages_spec.rb:130:in `block (4 levels) in <top (required)>'

3) UserPages edit page 
 Failure/Error: it { should have_title("Edit user") }
   expected #has_title?("Edit user") to return true, got false
 # ./spec/requests/user_pages_spec.rb:125:in `block (4 levels) in <top (required)>'

4) UserPages edit page 
 Failure/Error: it { should have_content("Update your profile") }
   expected #has_content?("Update your profile") to return true, got false
 # ./spec/requests/user_pages_spec.rb:124:in `block (4 levels) in <top (required)>'

5) UserPages edit page 
 Failure/Error: it { should have_link('change', href: 'http://gravatar.com/emails') }
   expected #has_link?("change", {:href=>"http://gravatar.com/emails"}) to return true, got false
 # ./spec/requests/user_pages_spec.rb:126:in `block (4 levels) in <top (required)>'

6) UserPages edit with valid information 
 Failure/Error: fill_in "Name",             with: new_name
 Capybara::ElementNotFound:
   Unable to find field "Name"
 # ./spec/requests/user_pages_spec.rb:138:in `block (4 levels) in <top (required)>'

7) UserPages edit with valid information 
 Failure/Error: fill_in "Name",             with: new_name
 Capybara::ElementNotFound:
   Unable to find field "Name"
 # ./spec/requests/user_pages_spec.rb:138:in `block (4 levels) in <top (required)>'

8) UserPages edit with valid information 
 Failure/Error: fill_in "Name",             with: new_name
 Capybara::ElementNotFound:
   Unable to find field "Name"
 # ./spec/requests/user_pages_spec.rb:138:in `block (4 levels) in <top (required)>'

9) UserPages edit with valid information 
 Failure/Error: fill_in "Name",             with: new_name
 Capybara::ElementNotFound:
   Unable to find field "Name"
 # ./spec/requests/user_pages_spec.rb:138:in `block (4 levels) in <top (required)>'

10) UserPages edit with valid information 
 Failure/Error: fill_in "Name",             with: new_name
 Capybara::ElementNotFound:
   Unable to find field "Name"
 # ./spec/requests/user_pages_spec.rb:138:in `block (4 levels) in <top (required)>'

Initially, I thought this was a Capybara issue, but I'm not sure about that. Here's the test code (found in ./spec/requests/user_pages_spec.rb):

describe "edit", :js => true do
  let(:user) { FactoryGirl.create(:user) }
  before do
    sign_in user
    visit edit_user_path(user)
  end

  describe "page" do
    it { should have_content("Update your profile") }
    it { should have_title("Edit user") }
    it { should have_link('change', href: 'http://gravatar.com/emails') }
  end

  describe "with invalid information" do
    before { click_button "Save changes" }
    it { should have_content('error') }
  end

  describe "with valid information" do
    let(:new_name)  { "New Name" }
    let(:new_email) { "[email protected]" }
    before do
      fill_in "Name",             with: new_name
      fill_in "Email",            with: new_email
      fill_in "Password",         with: user.password
      fill_in "Confirm Password", with: user.password
      click_button "Save changes"
    end

    it { should have_title(new_name) }
    it { should have_selector('div.alert.alert-success') }
    it { should have_link('Sign Out', href: signout_path) }
    specify { expect(user.reload.name).to  eq new_name }
    specify { expect(user.reload.email).to eq new_email }
  end
end

I added to the initial describe block :js => true because I thought it was a Capybara issue and the page was loading too fast, however, adding that hash caused FF browser to open and I could observe what was going on... FactoryGirl is successfully creating users, but gets an invalid email or password error when it tries to sign in. So it's failing to sign in, thus all the tests fail for the edit form. Here's the relevant FactoryGirl code:

sign_in() helper in ./spec/utilities.rb:

def sign_in(user, options={})
  if options[:no_capybara]
    # Sign in when not using Capybara.
    remember_token = User.new_remember_token
    cookies[:remember_token] = remember_token
    user.update_attribute(:remember_token, User.encrypt(remember_token))
  else
    visit signin_path
    fill_in "Email",    with: user.email
    fill_in "Password", with: user.password
    click_button "Sign In"
  end
end

./spec/factories.rb:

FactoryGirl.define do
  factory :user do
    sequence(:name)  { |n| "Person #{n}" }
    sequence(:email) { |n| "person_#{n}@example.com"}
    password "foobar"
    password_confirmation "foobar"

    factory :admin do
      admin true
    end
  end
end

I have the gem correctly installed and FactoryGirl appears to work fine with other tests, here's the gemfile:

group :test do
  gem 'selenium-webdriver', '2.35.1'
  gem 'capybara', '2.1.0'
  gem 'growl', '1.0.3'
  gem 'factory_girl_rails', '4.2.1'
end

Like I said, I took a look at the SQLite development and test databases and I don't see any users in the test db. Does FactoryGirl only temporarily create a user for the test and then rollback when done? What could cause a sign in failure like I'm seeing? Or is there something else I'm missing?

In case someone asks, here are the relevant views:

edit.html.erb:

<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>

<div class="row">
  <div class="span6 offset3">
    <%= form_for(@user) do |f| %>
    <%= render 'shared/error_messages' %>

    <%= f.label :name %>
    <%= f.text_field :name %>

    <%= f.label :email %>
    <%= f.email_field :email %>

    <%= f.label :password %>
    <%= f.password_field :password %>

    <%= f.label :password_confirmation, "Confirm Password" %>
    <%= f.password_field :password_confirmation %>

    <%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
  <% end %>

  <%= gravatar_for @user %>
  <a href="http://gravatar.com/emails">change</a>
</div>

And the _header.html.erb that is rendered in application.html.erb

<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="navbar-inner">
    <div class="container">
      <%= link_to "sample app", root_path, id: "logo" %>
      <nav>
        <ul class="nav pull-right">
          <li><%= link_to "Home", root_path %></li>
          <li><%= link_to "Help", help_path %></li>
          <% if signed_in? %>
            <li><%= link_to "Users", users_path %></li>
            <li id="fat-menu" class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                Account <b class="caret"></b>
              </a>
              <ul class="dropdown-menu">
                <li><%= link_to "Profile", current_user %></li>
                <li><%= link_to "Settings", edit_user_path(current_user) %></li>
                <li class="divider"></li>
                <li>
                  <%= link_to "Sign Out", signout_path, method: "delete" %>
                </li>
              </ul>
            </li>
          <% else %>
            <li><%= link_to "Sign In", signin_path %></li>
          <% end %>
        </ul>
      </nav>
    </div>
  </div>
</header>
1
Where is the #1 spec error? I'm assuming that's the one that has the issue with signing in? It's likely that errors 2-11 are caused by that, so if we can see / fix that, the others might fall into place.CDub
And yes, FactoryGirl creates the user for a specific test or block of tests if done in a before block inside a context/describe block. Once that is done, the factory is destroyed.CDub
Sorry, I should have stated, error 1 is not related to the same test, but may be caused by the same issue: 1) Authentication Signin page with valid information followed by signout Failure/Error: before { click_link "Sign Out" } Capybara::ElementNotFound: Unable to find link "Sign Out" # ./spec/requests/authentication_pages_spec.rb:41:in `block (5 levels) in <top (required)>'user2993894
No worries. I don't see an error with the user signing in. Where are you seeing this problem?CDub
When I run all of my tests via command line, once it gets to the describe 'edit' block, it launches firefox and I can watch what it does (since I added the hash :js => true). I watch it in real time attempt to sign in with FG generated users and get the 'invalid email or password' error.user2993894

1 Answers

1
votes

The first error you are getting is most likely because you need to have your describe "followed by signout" nested inside describe "after saving the user" like so:

 describe "after saving the user" do
        before { click_button submit }
        let(:user) { User.find_by(email: '[email protected]') }

        it { should have_link('Sign out') }
        it { should have_title(user.name) }
        it { should have_selector('div.alert.alert-success', text: 'Welcome') }
        describe "followed by signout" do #Notice that this describe is inside the other one
            before { click_link "Sign out" }
            it { should have_link('Sign in') }
        end
end

I know it is only partial answer but its hard to fix all the problems at once. Once you fix this problem run the test again and tell me what error codes stay. I'll try helping you further.

(at least if this solves your first problem, it could be something else)