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:
- Triple checked my code compared to the tutorial. Everything looks ok, but I may be missing something.
- Restarted my server and reset, re-populated, and re-setup the test db.
- 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>
before
block inside acontext
/describe
block. Once that is done, the factory is destroyed. – CDubdescribe '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