rspec before context without js - ruby-on-rails

In a feature test I have 2 feature macros: sign_in and add_to_cart. User logs in without JS, but for add_to_cart a JS macros is needed.
spec:
describe 'User add product in cart' do
before { sign_in(user) }
scenario 'and view cart stuff' do
add_to_cart(product, 2)
visit cart_path
end
end
feature macros
module FeaturesMacros
def sign_in(user)
visit sign_in_path
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Sign in'
end
def add_to_cart(product, quantity)
visit product_path(id: product.id)
fill_in 'line_item_quantity', with: quantity
click_button 'Add to Cart'
end
end
When I run test without JS, error is
ActionController::UnknownFormat: LineItemsController#create is missing a template for this request format and variant.
If I add JS for scenario 'and view cart stuff', js: true do, I have error in user login:
Failure/Error: visit sign_in_path.
How to add JS in scenario, but remove it from before context? Maybe there are better methods?

Related

Why do I get the error in my acceptance test?

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).

Within a feature spec, how to test that a Devise mailer is called successfully?

I have a feature test for user registration. How do I test that Devise confirmation instructions are sent correctly? I don't need to test the content of the email, only that the mailer has been called.
I am sending mails in the background.
#user.rb
def send_devise_notification(notification, *args)
devise_mailer.send(notification, self, *args).deliver_later
end
I have tried a few approaches that work for other mailers, including
it "sends the confirmation email" do
expect(Devise.mailer.deliveries.count).to eq 1
end
and
it "sends the confirmation email" do
message_delivery = instance_double(ActionMailer::MessageDelivery)
expect(Devise::Mailer).to receive(:confirmation_instructions).and_return(message_delivery)
expect(message_delivery).to receive(:deliver_later)
end
none of which are working as expected for Devise messages.
What am I doing wrong?
Edit
The feature spec looks like this:
feature "User signs up" do
before :each do
visit '/'
click_link 'Sign up'
fill_in 'user_email', with: valid_attributes[:email]
fill_in 'user_password', with: valid_attributes[:password]
fill_in 'user_password_confirmation', with: valid_attributes[:password]
click_button 'Sign up'
end
it "sends the confirmation email" ...
end
Since you're doing a high level feature spec, I would wager that as a result of clicking the 'Sign up' button, what you want to confirm is that an email job has been added to the queue.
In order to do that, you may have to slightly change your spec set up:
feature "User signs up" do
before :each do
visit '/'
click_link 'Sign up'
fill_in 'user_email', with: valid_attributes[:email]
fill_in 'user_password', with: valid_attributes[:password]
fill_in 'user_password_confirmation', with: valid_attributes[:password]
end
it "queues up a confirmation email job" do
expect { click_button 'Sign up' }.to \
have_enqueued_job(ActionMailer::DeliveryJob)
end
end
You can have a look at the have_enqueued_job matcher for more options if the above one doesn't quite suit your use case.

How to factor Capybara rspec testing code?

I need to test a system in which everything is available only after a user is signed in using Devise. Every time I use "it" I have to include the signup code.
Is there a way to factor the code below so that the "let's me make a new post" test and similar tests won't have to include the sign up?
describe "new post process" do
before :all do
#user = FactoryGirl.create(:user)
#post = FactoryGirl.create(:post)
end
it "signs me in" do
visit '/users/sign_in'
within(".new_user") do
fill_in 'Email', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_button 'Log in'
expect(page).to have_content 'Signed in successfully'
end
it "let's me make a new post" do
visit '/users/sign_in'
within(".new_user") do
fill_in 'Email', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_button 'Log in'
visit '/posts/new'
expect( find(:css, 'select#post_id').value ).to eq('1')
end
end
Your first option is to use the Warden methods provided, as per the documentation on this page:
https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
Your second option is just to login for real in your tests as you have done in your examples. You can streamline this though by creating some helper methods to do the login work rather than duplicating the code in all of your tests.
To do this, I would create a support directory within your spec directory, and then a macros directory within that. Then create a file spec/support/macros/authentication_macros.rb:
module AuthenticationMacros
def login_as(user)
visit '/users/sign_in'
within('.new_user') do
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
end
click_button 'Log in'
end
end
Next, update your RSpec config to load your macros. In either spec_helper.rb or rails_helper.rb if you're using a newer setup:
# Load your support files
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Include the functions defined in your modules so RSpec can access them
RSpec.configure do |config|
config.include(AuthenticationMacros)
end
Finally, update your tests to use your login_as function:
describe "new post process" do
before :each do
#user = FactoryGirl.create(:user)
#post = FactoryGirl.create(:post)
login_as #user
end
it "signs me in" do
expect(page).to have_content 'Signed in successfully'
end
it "let's me make a new post" do
expect( find(:css, 'select#post_id').value ).to eq('1')
end
end
Obviously, make sure you have password defined in your user factory.

Trouble with refactoring code for rspec feature tests in rails 4

I am trying to give the user of my web app the ability to login with a password. I am rolling my own authentication instead of using a gem. I read this article about refactoring Rspec/Capybara tests:
http://robots.thoughtbot.com/rspec-integration-tests-with-capybara
I liked what I read and decided to give refactoring a try. I created a session helper file for my feature tests.
module Features
module SessionHelpers
def sign_in
user = create(:user)
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
end
I then called the sign_in function in my login tests. Here is a little sample.
require 'spec_helper'
feature "signing in" do
before :each do
User.create(:name => 'user#example.com', :password => 'caplin')
end
scenario "user who logs in with correct credentials" do
sign_in
expect(page).to have_content 'Hi user#example.com'
end
end
Unfortunately, I keep getting this error message:
2) signing in user who logs in with correct credentials
Failure/Error: sign_in
NoMethodError:
undefined method `create' for #<RSpec::Core::ExampleGroup::Nested_3:0x007ffc85012438>
# ./spec/support/features/session_helpers.rb:4:in `sign_in'
# ./spec/features/user_logs_in_spec.rb:13:in `block (2 levels) in <top (required)>'
Basically, I need some way to grab the user I created and pass it into the sign_in function. Any hints?
I'm guessing your first issue is a different test configuration than the one the ThoughBot example has. create is not to my knowledge a default method available in RSpec; I'm going to guess they've added every FactoryGirl method to the testing scope. If you're using FactoryGirl, you can get the same behavior by just namespacing the create command:
def sign_in
user = FactoryGirl.create(:user)
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
However, this won't quite get you everything that you asked for, since you still won't be able to add a custom user. An easy way for this would allow for a user to be passed in:
def sign_in(user=nil)
user ||= FactoryGirl.create(:user)
...
end
This will create the user for you if you don't pass one in on the sign_in call.
Going back to the spec you posted, you'd want to change it to this:
feature "signing in" do
before :each do
#user = User.create(:name => 'user#example.com', :password => 'caplin')
end
scenario "user who logs in with correct credentials" do
sign_in(#user)
expect(page).to have_content 'Hi user#example.com'
end
end
You'd need to attach the user you created to a variable (#user), then pass it to the sign_in as needed.
Problem in you model
module Features
module SessionHelpers
def sign_in
user = create(:user) # <- this method allow only in FactoryGirl
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
end
i use another way. Create a class and include FactroyGirl methods and Capybara::DSL like this
class Features
include FactoryGirl::Syntax::Methods
include Capybara::DSL
def sign_in
user = create(:user) #<- FactroyGirl
visit '/authentications/new' #<- Capybara
fill_in 'Login', with: user.name #<- Capybara
fill_in 'Password', with: user.password #<- Capybara
click_button 'Sign in' #<- Capybara
self #<- return page
end
end
in spec
feature "signing in" do
let(:login_user) { Features.new }
scenario "user who logs in with correct credentials" do
page = login_user.sign_in
expect(page).to have_content 'Hi user#example.com'
end
end
You can accomplish this by including FactoryGirl in your tests. Your RSpec configuration block (in spec_helper.rb or in the new version of RSpec rails_helper.rb) should look like this:
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end

Rails Factory Girl "Create" not saving record?

I have two capybara tests that are testing the signup process on my rails application, both using factory girl. One is just using Factory Girl build command and saving it with the form:
it 'should create a user and associated customer_info', js: true do
visit signup_path
user = build(:user)
customer = build(:customer_info)
sign_up user, customer
page.should have_content 'Welcome back, ' + customer.firstname
end
Whereas the other is using the create command, and then attempting to sign in with that info.
it 'should be able to sign in', js: true do
user = create(:user)
customer = create(:customer_info, user_id: user.id)
visit new_user_session_path
fill_in 'user_email', with: user.email
fill_in 'user_password', with: user.password
click_button 'Sign in'
page.should have_content 'Welcome back, ' + customer.firstname
end
The first one passes and saves in my test database. The second one fails, saying "invalid email or password," but also when I check my database after each test, the first one saves a record but the second one doesn't (which I'm assuming is why it's saying invalid email/password).
Any ideas why my FactoryGirl create function isn't actually saving my record in the database?
EDIT
I have a sequence in my FactoryGirl definition for the email, and build AND create both increase the sequence, so it shouldn't be creating dups, right?
FactoryGirl.define do
factory :user do
sequence(:email) { |n| "foo#{n}#example.com"}
password "secret"
password_confirmation "secret"
end
end
The problem is you are trying to create duplicate user. Sign up creates user in test database and now when you are trying to create a new user with FactoryGirl it would raise validation error because the same user is already there in test database. You should do something like this:
def create_user
#user ||= create(:user)
end
it 'should create a user and associated customer_info', js: true do
visit signup_path
#user = build(:user)
customer = build(:customer_info)
sign_up #user, customer
page.should have_content 'Welcome, ' + customer.firstname
end
it 'should be able to sign in', js: true do
create_user
customer = create(:customer_info, user_id: #user.id)
visit new_user_session_path
fill_in 'user_email', with: #user.email
fill_in 'user_password', with: #user.password
click_button 'Sign in'
page.should have_content 'Welcome back, ' + customer.firstname
end
May be you can use the different approach to solve it. but main focus is on to use the single user object for sign up and sign in.
Hope this would help you.

Resources