I have a very simple RSpec Capybara have_selector() that doesn't seem to be working and I can't figure out why... but probably something simple.
Here's the RSpec request test:
describe 'with valid information'
let(:user) { FactoryGirl.create(:user) }
before do
fill_in 'Email', with: user.email
fill_in 'Password, with: user.password
click_button 'Login'
end
it { should have_selector('p', text: user.first_name) }
...
end
In the new.html.erb for the session, I just have something like this:
<% if logged_in? %>
<p><%= current_user.first_name %></p>
<% else %>
<%= form_for(:session, url: sessions_path) do |f| %>
...
<% end %>
The login form session#new works perfectly in the browser. I can login just fine and am able to view the first name in a p tag. The problem seems to be the capybara part, because the other it test checking for the h1 tag on the page (which is present regardless of being logged in) works fine.
Does anyone know what the problem could be?
Thanks in advance for any help!!
More than likely, your login is failing for one reason or another.
Change your test to look like this:
it "should show me my first name" do
save_and_open_page
should have_selector('p', text: user.first_name)
end
That will open your browser and show you the page as the test browser currently sees it. (You may need to add the launchy gem to your Gemfile.)
There are a few other diagnostics that would help, for instance, adding a test or a puts that checks what page you landed on, such as:
it "redirects to the home page after log in" do
current_path.should == root_path
end
fill_in 'Password, with: user.password
not like that. You should use this line,
fill_in 'Password', with: user.password
Related
Tell me please,why does it happen?
I can't understand, if I write:
feature "Article Creation" do
#here i write (:all)
before(:all) do
sign_up_helper
end
I get the error:
Article Creation allows user to visit to creating article page
Failure/Error: fill_in :article_title, :with => 'test_title'
Capybara::ElementNotFound:
Unable to find field :article_title
or
1) Article Creation allows user to visit to article page
Failure/Error: expect(page).to have_content I18n.t('articles.articles_new')
expected to find text "New Article:" in "Toggle navigation Blog Rails New Contacts Sign in --- !ruby/hash:ActionController::Parameters controller: devise/sessions action: new {\"controller\"=>\"devise/sessions\", \"action\"=>\"new\"} nil You need to sign in or sign up before continuing. Sign in: Email Password Remember me Sign up Forgot your password?"
but, if I write:
feature "Article Creation" do
#here i write(:each)
before(:each) do
sign_up_helper
end
It's Ok. All tests works. My question -WHY?
This is my test:
*#before all test visitor signs up
#here I've changed :all and :each*
feature "Article Creation" do
before(:all) do
sign_up_helper
end
scenario "allows user to visit to article page" do
visit new_article_path
expect(page).to have_content I18n.t('articles.articles_new')
end
scenario "allows user to visit to created article page" do
visit new_article_path
fill_in :article_title, :with => 'test_title'
fill_in :article_text, :with => 'example_text'
click_button 'Save Article'
expect(page).to have_content 'example_text'
end
This is sign_up_helper method:
#spec/support/session_helper.rb
def sign_up_helper
visit new_user_registration_path
fill_in :user_email, :with => 'user#example.com'
fill_in :user_username, :with => 'mike'
fill_in :user_password, :with => 'secure123!##'
fill_in :user_password_confirmation, :with => 'secure123!##'
click_button 'Sign up'
end
This is html form:
<p>
<label for="article_title">Title</label><br/>
<input type="text" name="article[title]" id="article_title" />
</p>
<p>
<label for="article_text">Text</label><br/>
<textarea name="article[text]" id="article_text">
</textarea>
</p>
Environment for each test is set anew, I think. New session, cookies, etc. In many cases, even brand new users are generated. So one "global" login is not possible.
Even if it were possible, it would still be a problem, as it introduces spec order dependency which is bad. Imagine that one of your specs logs user out. Then each subsequent spec would fail, because user is not logged in anymore.
To prevent this, make sure that each spec sets its own environment as it needs it (user logins, method stubs, etc.), without relying on side-effects from previously executed specs (which may or may not persist).
Quick summary: why can't capybara find the .admin-edit class?
So, I have built a site where there are published and unpublished articles and only the published articles are seen by guests while admins can see everything. Login is handled through devise and a simple erb expression determines if an article is shown or 'published'.
I list articles on the index action of my articles controller and render a partial to display the articles.
<% if article.published %>
<dl class="individual-article">
<dt><%= article.title %>
<% if current_user.try(:admin) %>
| <span class="admin-edit"><%= link_to 'Edit', edit_article_path(article) %></span>
<% end %><br>
<span class="article-tags">
<%= raw article.tags.map(&:name).map { |t| link_to t, tag_path(t) }.join(', ') %></span>
</dt>
<dd><%= truncate(article.body.html_safe, length: 200) %>
<%= link_to 'more', article_path(article) %>
</dd>
</dl>
<% end %>
This works as expected but I cannot test for it correctly. In particular, it returns false on expecting to find 'Edit' if the user is admin.
Here is my sign_in_spec:
require 'rails_helper'
RSpec.describe "SignIns", type: :request do
describe "the sign in path" do
let(:user) { FactoryGirl.create(:user) }
let(:admin) { FactoryGirl.create(:admin) }
let(:article) { FactoryGirl.create(:article) }
let(:published) { FactoryGirl.create(:published) }
it "lets a valid user login and redirects to main page" do
visit '/users/sign_in'
fill_in 'user_email', :with => admin.email
fill_in 'user_password', :with => admin.password
click_button 'Log in'
expect(current_path).to eq '/'
expect(page).to have_css('span.admin-edit')
end
end
And here is my article factory:
FactoryGirl.define do
factory :article do
title 'Title'
body 'Content'
factory :published do
published true
end
end
And here is my user factory:
FactoryGirl.define do
factory :user do
email 'user#gmail.com'
password 'password'
factory :admin do
admin true
end
end
end
Here is the error:
1) SignIns the sign in path lets a valid user login and redirects to main page
Failure/Error: expect(page).to have_css('span.admin-edit')
expected #has_css?("span.admin-edit") to return true, got false
# ./spec/requests/sign_ins_spec.rb:18:in `block (3 levels) in <top (required)>'
I have tried the following:
Eliminating the extra article if rspec had a problem with multiple classes
Changing have_css to have_selector and selecting the anchor tag
Drawing out the entire DOM root from html body ...
Checking if it was working outside of the spec by manually logging in as user with admin privs -> it does.
Tried deleting unpublished vs published article distinction but still fails.
Tried removing erb condition to check if article is published in view but still fails.
Tried making sure it wasn't loading via ajax (has backup in will_paginate) but fails.
What am I doing wrong?
Edit
It now works if I avoid using the FactoryGirl importing:
#article = Article.create(title: 'Title', body: 'body', published: true)
Instead of
let(:published) { FactoryGirl.create(:published) }
No idea why.
RSpec lazily assigns let variables, so at the time you display your page, neither the published nor unpublished articles exist. You need to use let! or before or otherwise ensure the objects get created prior to display of your page.
I can sign in with my browser but Cucumbe can't sign in.
My users/sign_out route accepts GET.
features/step_denfinitions/home_page_steps.rb:
When(/^I am on the homepage$/) do
visit root_path
end
Then (/^I should see "(.*?)"$/) do |t|
assert page.has_content?(t)
end
Given (/^I am signed in$/) do
visit ('/users/sign_out')
email = 'game#maker.com'
password = 'password'
visit '/users/sign_in'
fill_in "user_email", :with => email
fill_in "user_password", :with => password
click_button "Sign in"
end
And(/^I go to home page$/) do
visit root_path
end
features/home_page.feature:
Feature: Home page
Scenario: Viewing home page
Given I am on the homepage
Then I should see "Hello"
Scenario: Signed in can create a game
Given I am signed in
Then I should see "Create Game"
/app/views/welcome/index.html (my root path):
<h1>Welcome#index</h1>
"Hello"
<p>Find me in app/views/welcome/index.html.erb</p>
<% if user_signed_in? %> <<< This works in my browser but not for cucumber
<%= link_to "Create Game", new_game_path %>
<% end %>
Ok, Cucumber doesn't actually sign in with a real user.. I have to create the user on the fly.
Testing login with devise and cucumber
I've been following the Rails Tutorial by Michael Hartl. Actually I already finished it, but I am having some problems with some refactoring I did for the last exercise of the final chapter. All I did was changing the show view for the user model (show.html.erb) from this:
<section>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
To this:
<section>
<%= render 'shared/user_info' %>
</section>
And in the partial(_user_info.html.erb) I have this:
<a href="<%= user_path(current_user) %>">
<%= gravatar_for current_user, size: 52 %>
</a>
<h1> <%= current_user.name %> </h1>
<% unless current_page?(user_path(current_user)) %>
<span> <%= link_to "view my profile", current_user %> </span>
<span> <%= pluralize(current_user.microposts.count, "micropost") %> </span>
<% end %>
Everything works fine on the browser, but for some reason rspec is failing some tests, I suspect rspec is having problems calling the current_user method which is defined in sessions_helper.rb, which by the way is included in application_helper.rb. The gravatar_for function is defined in users_helper.rb. Here is the error I get from the tests:
Failure/Error: before { visit user_path(user) }
ActionView::Template::Error:
undefined method email' for nil:NilClass
# ./app/helpers/users_helper.rb:4:ingravatar_for'
# ./app/views/shared/_user_info.html.erb:1:in _app_views_shared__user_info_html_erb___3480157814439046731_47327400'
# ./app/views/users/show.html.erb:5:in_app_views_users_show_html_erb___1252254778347368838_47378900'
# ./spec/requests/user_pages_spec.rb:58:in `block (3 levels) in '
I would appreciate if you could help me identify what is going on here. I could find different ways to do the same thing but I am just very curious about this. I am not very experienced in Rails which is why I followed the tutorial so forgive me if I am missing something obvious. Thanks.
I think I got the solution. Although I am still confused. Here is the code for the tests as they were before the refactoring mentioned in my question (as they are in the tutorial, they all were passing):
describe "profile page" do
let(:user) { FactoryGirl.create(:user) }
let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") }
let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") }
before { visit user_path(user) }
it { should have_selector('h1', text: user.name) }
it { should have_selector('title', text: user.name) }
describe "microposts" do
it { should have_content(m1.content) }
it { should have_content(m2.content) }
it { should have_content(user.microposts.count) }
end #some other tests for show page continue, also having the same behaviour
end
While reading the tests to see if there was a mistake I started wondering why those tests were passing if I was not signing the user in, so I added the code to sign in the user in the before block, like this:
describe "profile page" do
let(:user) { FactoryGirl.create(:user) }
let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") }
let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") }
before do
valid_signin user
visit user_path(user)
end
it { should have_selector('h1', text: user.name) }
it { should have_selector('title', text: user.name) }
describe "microposts" do
it { should have_content(m1.content) }
it { should have_content(m2.content) }
it { should have_content(user.microposts.count) }
end #some other tests for show page continue, also having the same behaviour
end
And now all tests pass. I know is seems silly and obvious to sign in the user, however that's how the tests are in the tutorial and they were working before. Here is the updated test file if you want to check it. All changes are now committed in my github repo. Thank you all for your help.
Poking around your repo, this may be the problem: there was a minor bug reported on June 25 in the Rails Tutorial with regards to the setting of current_user in the sign_in and sign_out methods in the app/helpers/sessions_helper.rb file. The notice doesn't seem to have been posted on the Rails Tutorial News site, but it was sent to subscribers and it is reflected in the current online book saying to change:
self.current_user = user # in the sign_in method
self.current_user = nil # in the sign_out method
So, try updating your sessions_helper.rb, and see if that stops you from getting a nil user.
Sounds like you don't have any users in your test database. The development database and test database are two different things. Rspec uses the test database when it's running tests.
To setup the test database you do rake db:test:prepare.
Then you need to populate the test database when you run your specs. One great way to do that is with the Factory Girl gem.
I have no idea why it's not working. After hours of trying to figure this out I wrote a small test to check if the ActionMailer::Base.deliveries was empty and it was.
When I test my reset form it works and mail is sent but when I run test it doesn't seem to store anything in the array.
require 'spec_helper'
describe "Passwords" do
describe "reset password" do
it "emails user when requesting password reset" do
visit login_path
click_link "Forgot Password?"
response.should render_template "passwords/new"
response.should have_selector :h1, :content => "Reset Password"
fill_in "password_reset[email]", :with => "foobar#gmail.com"
click_button "Reset Password"
response.should render_template "users/new"
response.should have_selector :div, :id => "flash_notice", :content => "Email sent with password reset instructions."
ActionMailer::Base.deliveries.empty?.should be_true
# mail = ActionMailer::Base.deliveries.last
end
end
end
Just found out a great gem to test emails with rspec: https://github.com/bmabey/email-spec
Hope it will help.
I always seem to do silly things like this:
Because I created the passwords_spec file manually and not using the rspec generator I forgot to add "require 'spec_helper'" at the top of the file. Something that the generator would have done automatically.
The last few days I have been figuring out my silly mistakes. Funny how all this happened when I got lazy and decided to build my app first then test after. Well I've learnt from my mistake. Test first!