I use db/seeds.rb to populate my database with 2 user roles ("Admin", "User") that will never change. When i run tests though, the seed data does not get carried over and the results are error tests.
When i try to run cucumber i get the following error:
Using the default profile... Feature: Sign in In order to get access
to protected sections of the site A valid user Should be able to
sign in
Scenario: User is not signed up #
features/users/sign_in.feature:6 Not registered: role
(ArgumentError)
/Users/x/.rvm/gems/ruby-1.9.2-p180/gems/factory_girl-2.0.0.rc4/lib/factory_girl/registry.rb:15:in
find'
/Users/x/.rvm/gems/ruby-1.9.2-p180/gems/factory_girl-2.0.0.rc4/lib/factory_girl.rb:39:in
factory_by_name'
/Users/x/.rvm/gems/ruby-1.9.2-p180/gems/factory_girl-2.0.0.rc4/lib/factory_girl/syntax/vintage.rb:53:in
default_strategy'
/Users/x/.rvm/gems/ruby-1.9.2-p180/gems/factory_girl-2.0.0.rc4/lib/factory_girl/syntax/vintage.rb:146:in
Factory'
/Users/x/rails/ply_rails/features/support/db_setup.rb:6:in
`Before'
Given I am not logged in #
features/step_definitions/user_steps.rb:36
Here is what my setup looks like:
# features/support/db_setup.rb
Before do
# Truncates the DB before each Scenario,
# make sure you've added database_cleaner to your Gemfile.
DatabaseCleaner.clean
Factory(:role, :name => 'Admin')
Factory(:role, :name => 'User')
end
# features/users/sign_in.feature
Feature: Sign in
In order to get access to protected sections of the site
A valid user
Should be able to sign in
Scenario: User is not signed up # THIS IS LINE 6
Given I am not logged in
And no user exists with an email of "user#user.com"
When I go to the sign in page
And I sign in as "user#user.com/password"
Then I should see "Invalid email or password."
And I go to the home page
And I should be signed out
# features/step_definitions/user_steps.rb
Given /^I am a "([^"]*)" named "([^"]*)" with an email "([^"]*)" and password "([^"]*)"$/ do |role, name, email, password|
User.new(:name => name,
:email => email,
:role => Role.find_by_name(role.to_s.camelize),
:password => password,
:password_confirmation => password).save!
end
Not sure where to start on getting this working, thank you for your help/time!
Well the point of tests is to start with a clean database, i.e a consistent state, so it's kind of good that everything gets wiped.
Secondly, in terms of cucumber, you should be defining a Background block to do the set up. This will run for each scenario, and has the benefit of every action being explicitly known. This is especially useful if you use the cucumber scenario plain text to show to clients. So you should do something like:
Background:
Given that the role "Admin" exists
And that the role "User" exists
Scenario:
etc
And make custom steps for the that the role [blank] exists that will create the role for you.
Related
I'm currently trying to write a rake task that resets my database while copying admins (specific type of devise user) to the new server.
task :safe_reset => :environment do
desc "Resets db while persisting admins."
user_collection = []
User.all.each do |user|
if user.admin?
user_collection << user.attributes
end
end
Rake::Task['db:reset'].invoke
user_collection.each do |user|
User.create!(user)
end
end
However, password information is not a public attribute of user. So I don't have enough information to create a new user essentially.
Is there a way to get password information, or preferably, is there a way to do this while avoiding reducing every admin to a hash object?
You can't get the original password information. But you can get the encrypted password...
Replace
user_collection.each do |user|
User.create!(user)
end
By this
user_collection.each do |user|
encrypted_password = user.delete('encrypted_password')
u = User.create!(user.merge({
:password => "Foobar",
:password_confirmation => "Foobar"
})
u.update_attribute(:encrypted_password, encrypted_password)
end
This shouldn't be done. Typically an application is 'seeded' (in db/seeds.rb) with one administrative user account. After the application goes live a developer uses that account to propagate the other necessary accounts and changes its details to something other than the defaults. I don't see any advantage or benefit to persisting one table like this as db:reset is generally never used in production or staging and is often used in development. In development we use our seed data to generate accounts for us.
It can be done, depending on your auth solution but I highly recommend against the practice unless there's a good use case for it.
I have just set up Pickle with Factory girl like this (features/support/pickle.rb):
Pickle.configure do |config|
config.adapters = [:factory_girl]
config.map 'I', 'myself', 'me', 'my', :to => 'user: "me"'
end
And I now want to start a cucumber scenario like this:
Given a journal exists with user: me
So that it creates an instance of the journal model with the current user as the user_id, but I get this error:
Pickle::Session::ModelNotKnownError: The model: 'user: "me"' is not known in this
scenario. Use #create_model to create, or #find_model to find, and store a reference
in this scenario.
How do I get Pickle to match 'me' to current_user? The hint in the error message doesn't tell me how or where to store it.
n.b. The login step I have made already stores the current user in #me
It's OK - I found it. Add this to the 'I am logged in' step:
store_model('user', 'me', #me) # Where #me is the logged in user record
I integrated devise with facebook. Now when I create a user account after the user has logged in with his/her facebook account,
user = User.create(:email => data["email"],
:password => Devise.friendly_token[0,20])
user.confirmed_at = DateTime.now
user.save!
even though the account has been confirmed, an confirmation email is still fired. Any idea how I can turn the email firing off?
The confirm callback happens after create, so it's happening on line 1 of your example, before you set confirmed_at manually.
As per the comments, the most correct thing to do would be to use the method provided for this purpose, #skip_confirmation!. Setting confirmed_at manually will work, but it circumvents the provided API, which is something which should be avoided when possible.
So, something like:
user = User.new(user_attrs)
user.skip_confirmation!
user.save!
Original answer:
If you pass the confirmed_at along with your create arguments, the mail should not be sent, as the test of whether or not an account is already "confirmed" is to look at whether or not that date is set.
User.create(
:email => data['email'],
:password => Devise.friendly_token[0,20],
:confirmed_at => DateTime.now
)
That, or just use new instead of create to build your user record.
If you just want to prevent sending the email, you can use #skip_confirmation_notification, like so:
user = User.new(your, args)
user.skip_confirmation_notification!
user.save!
See documentation
Skips sending the confirmation/reconfirmation notification email
after_create/after_update. Unlike #skip_confirmation!, record still
requires confirmation.
Open up the Rails console
rails c
Note the user (through id) or using rails helper methods, eg. first, last.
Create a variable to hold the user.
user = User.last
Use the skip_confirmation helper to confirm the user, then save.
user.skip_confirmation
user.save
As best I can tell cucumber is only hitting the database once between these two scenarios, but it's clearing out the database between scenarios.
The Features:
Feature: a new user vists the site and signs up
in order to get new users
when an unlogged in users comes to the website
then they should see the sign-up dialog
and be able to signup for the website
Background:
Given I have at least one deal
Scenario: a new user is asked to signup
Given I am on show deal
Then I should see "New Here?"
#javascript
Scenario: new user signup failure
Given I am on show deal
When I fill in "consumer[user_attributes][email]" with "test#test.com"
And I press "consumer_submit"
Then I should see "1 error prohibited"
The Step Definition:
Given /^I have at least one deal$/ do
Deal.create copy:'Example Deal Copy', copy_header:'Example Deal Header', copy_subheader:'Example Deal Subheader' if Deal.all.size == 0
end
The Result:
Background: # features/new_user_signup.feature:7
Given I have at least one deal # features/step_definitions/new_user_signup_steps.rb:1
Scenario: a new user is asked to signup # features/new_user_signup.feature:10
Given I am on show deal # features/step_definitions/web_steps.rb:44
Then I should see "New Here?" # features/step_definitions/web_steps.rb:105
#javascript
Scenario: new user signup failure # features/new_user_signup.feature:15
Given I am on show deal # features/step_definitions/web_steps.rb:44
Couldn't find Deal with ID=1 (ActiveRecord::RecordNotFound)
./app/controllers/deals_controller.rb:17:in `show'
<internal:prelude>:10:in `synchronize'
./features/step_definitions/web_steps.rb:45:in `/^(?:|I )am on (.+)$/'
features/new_user_signup.feature:16:in `Given I am on show deal'
When I fill in "consumer[user_attributes][email]" with "test#test.com" # features/step_definitions/web_steps.rb:60
And I press "consumer_submit" # features/step_definitions/web_steps.rb:52
Then I should see "1 error prohibited" # features/step_definitions/web_steps.rb:105
Failing Scenarios:
cucumber features/new_user_signup.feature:15 # Scenario: new user signup failure
Whichever scenario I put second will give the ActiveRecord error. Why are there no records in the database for my second scenario?
Now I know how you've mapped "show deal" I am tempted to say that the problem is that the Deal instance possibly exists but it's id is not equal 1. Can you check please?
And here is a tip: while you're defining paths in your path.rb, you may do something like this:
when /the edit deal page/
edit_deal_path(Deal.first)
or even this:
when /the deal page for deal named ".*"/
deal_name = page_name.scan(/".*"/).first.gsub("\"", '')
deal = Deal.find_by_name(deal_name)
deal_path(deal)
As long as you've defined your "I am on" webstep like this:
Given /^(?:|I )am on (.+)$/ do |page_name|
visit path_to(page_name)
end
It's far better than "deals/1" :)
Note: I am very new to Cucumber.
I am trying to make a generalized step (not sure if one already exists somewhere or not) so that you can easily add objects to another object, given the association exists. I want to do something like:
manage_notes.feature
Background: Login User
Given the following user records
| email | password |
| test#email.com | password |
Given I am logged in as "test#email.com" with password "password"
Scenario: Edit existing note
Given I have already created a note that belongs to current_user
general_steps.rb
Given /^the following (.+) records?$/ do |factory, table|
table.hashes.each do |hash|
Factory(factory, hash)
end
end
Given /^I am logged in as "([^\"]*)" with password "([^\"]*)"$/ do |email, password|
unless email.blank?
visit new_user_session_path
fill_in "Email", :with => email
fill_in "Password", :with => password
click_button "Sign In"
end
end
note_steps.rb
Given /^I have already created a (.+) that belongs to (.+)$/ do |factory, resource|
model = Factory(factory)
resource.send(model.class.to_s.downcase.pluralize) << model
end
Seems like there might be a way to use the devise 'current_user' helper.
What is the correct way to accessing the user that is logged in?
Please let me know if you need more information.
Thanks!
UPDATE 1:
I have temporarily fixed my issue by creating a new step that allows me to do:
Given I have already created a note that is owned by the user with email "test#email.com"
But I don't want to specify the email, I'd still like to be able to use the logged in user if possible.
UPDATE 2:
Added general_steps.rb
So you can see, that in my 'Background', the user is created via a Factory, and then is logged in via my interface. I want to access the model of that logged in User.
I don't use Devise, so I can't answer specifically to if Devise has method of access the current_user.
But I actually like to use Pickle to help me keep my references. And perhaps this can help you out till you find a more Devise specific way to achieve what you want.
Given /^the following (.+) records$/ do |factory, table|
table.hashes.each do |hash|
Factory(factory, hash)
# since this is like an all encompassing factory creator, this line to
# create a Pickle references might need a bit more extra work if you want
# to create references for other factory types
# I assume email is unique, else use a unique identifier of some sort
find_model! %{user: "#{hash['email']}"}, {:email => hash['email']}
end
end
Given /^I have already created a (.+) that belongs to #{capture_model}$/ do |factory, name|
model = Factory(factory)
ref = model!(name) # we find that reference here
ref.send(model.class.to_s.downcase.pluralize) << model
end
This would read
Given I have already created a note that belongs to user: "test#email.com"
# I would just change this to
Given I have already created a note
# or
Given a note was created for user: "test#email.com"
You are I since you said Given I logged in..., no need to say that belongs to user: "test#email.com" it's already you.
Not to mention it could lead to confusion when you read it, some people may think you are adding a note to a user, who they might now know (or realize) is actually yourself.
While you still have to reference explicitly (eg. user: "John Doe"), I think that is a plus. By always calling specific references, everyone knows who is being referenced and there is no question about who is doing what to what.
Pickle serves us very well for this purpose. The only problematic areas we find are with things created directly through the app's ui, which gets a bit tricky to ensure you are creating the right reference to it.
Pickle has a large number of uses so definitely take a look.
Upate
You will have to find yourself here. Since, like you wanted, there is no current_user option (as far as we know). So you basically have to go find the "actor" in this scenario.
Given /^I have already created a note$/ do
user = model!(%{user: "test#email.com"}) # if using pickle
# user = User.find_by_email("test#email.com") # if just regular AR
user.notes << Note.create
end
I just solve it very simple:
I have "FactoryGirl" user defined, then this method...
Given(/^I am users logged in as "(.*?)" with password "(.*?)"$/) do |email, pass|
visit new_user_session_path
fill_in "user_email", :with => email
fill_in "user_password", :with => pass
click_button I18n.t("login.sign_in")
#current_user = User.find_by_email(email)
end
further on You could use #current_user in Your steps
This topic is quite old, but here is the solution I use. You must load this module, in env.rb, for example. Using it you can access the current user using either #current_user or current_user in your steps.
module UserSessionHelper
def current_user
#current_user
end
def login(user=nil)
if user.nil?
#current_user = FactoryGirl.create(:user, username: fake_name)
else
#current_user = user
end
visit login_path
fill_in 'Username', with: #current_user.username
fill_in 'Password', with: '1234'
click_button 'Login'
page.should have_content('Logout')
end
def logout
click_link 'Logout'
end
end
RSpec.configure do |config|
config.include UserSessionHelper
end if RSpec.respond_to?(:configure)
World(UserSessionHelper) if respond_to?(:World)
You should be able to use context for this:
http://cuke4ninja.com/sec_sharing_context.html
Have a step where you login as a user, store it in a shared context accessible to all steps and then just access it in following steps.
Hope this makes steps and I did not misinterpret the question.
Good luck with that!