I have the following test...
spec/features/users/sign_in_spec.rb
require "rails_helper"
feature "User sign in" do
extend SubdomainHelpers
let!(:account) { FactoryGirl.create(:account) }
let(:sign_in_url) { "http://#{account.subdomain}.example.com/sign_in" }
let(:root_url) { "http://#{account.subdomain}.example.com/" }
within_account_subdomain do
scenario "signs in as an account owner successfully" do
visit root_url
expect(page.current_url).to eq(sign_in_url)
fill_in "Email", :with => account.owner.email
fill_in "Password", :with => "password"
click_button "Sign in"
expect(page).to have_content("You are now signed in.")
expect(page.current_url).to eq(root_url)
end
end
end
Note the 3rd line extend SubdomainHelpers which I am trying to load from...
spec/support/subdomain_helpers.rb
module SubdomainHelpers
def within_account_subdomain
let(:subdomain_url) { "http://#{account.subdomain}.example.com" }
before { Capybara.default_host = subdomain_url }
after { Capybara.default_host = "http://www.example.com" }
yield
end
end
When I run the text I get the error uninitialized constant SubdomainHelpers (NameError) How do I call the SubdomainHelpers module from the test?
You need to require 'support/subdomain_helpers' in your rails_helper or the sign_in_spec file.
Files in the spec/support subdirectory are automagically loaded into LOAD_PATH by rspec, but they're not required.
Related
I cannot get rspec to load support files, have tried:
config.include SessionHelper, :type => :controller
In the spec_helper file, Which gives me
NameError: uninitialized constant SessionsSpecHelper
Adding:
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
Gives me:
NoMethodError: undefined method `join' for nil:NilClass
Adding:
require 'support/session_helpers.rb'
Provides no difference
support/session_helper.rb:
module SessionHelpers
def sign_up_with(email, password)
visit sign_up_path
fill_in 'Email', with: email
fill_in 'Password', with: password
click_button 'Sign up'
end
def sign_in
user = create(:user)
visit sign_in_path
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
Have you noticed the error config.include SessionsHelper, :type => :controller
has Sessions
while your module module SessionHelpers has Session
I have been trying, unsuccessfully, refactor some rspec features using Page Objects, but I'm having a hard time with it.
I'm not a experienced RoR developers. Probably this is a basic error.
I have a spec feature like this:
require 'capybara/rspec'
feature 'Acessing the dashboard' do
scenario 'User see the dashboard after login' do
sign_in 'teste','123'
expect(page).to have_css 'h1', text: 'Dashboard'
end
def sign_in(login, password)
visit '/'
fill_in 'login', :with => login
fill_in 'password', :with => password
click_button 'Autenticar'
end
end
But, I would like to share my sign_in method with another features through a Login Page Object like this:
class LoginPage
include Capybara::DSL
def sign_in(login, password)
visit '/'
fill_in 'login', :with => login
fill_in 'password', :with => password
click_button 'Authenticate'
end
end
So, I changed my feature:
feature 'Acessing the dashboard' do
let(:login_page) { LoginPage.new }
scenario 'User see the dashboard after login' do
login_page.sign_in 'teste','123'
expect(page).to have_css 'h1', text: 'Dashboard'
end
end
But I receive the following error:
Failure/Error: let(:login_page) { LoginPage.new }
NameError: uninitialized constant LoginPage
I really don't know what to do.
I created the LoginPage class in /spec/support/LoginPage.rb. Is it correct? Should I put this class in other path?
Somebody can help me to deal with it?
Thanks a lot.
You have to require your support folder files in you spec_helper.rb file
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
In my app, I have an identical form on two separate pages. I've written a series of tests for the one page, and would like to simply loop over the test suite for my second page, passing in the page for which the test suite should run against. The problem is, I can't seem to access the url_helpers in a clean way.
This is what I have so far that works:
require 'rails_helper'
describe "my tests" do
subject { page }
paths = ["/signin", "/signup"]
paths.each do |path|
describe "user signs in via #{path}" do
before { visit path }
describe "user enters invalid information" do
before do
fill_in "Email", with: user.email, match: :first
fill_in "Password", with: user.password.reverse, match: :first
click_button "Sign in"
end
it { should have_title("Sign in") }
it { should have_content "Invalid email/password combination!" }
its(:current_path) { should eq "/signin" }
end
end
end
If I replace "/signin" with signin_path, I get
undefined local variable or method `signin_path' for RSpec::ExampleGroups::Authentication:Class (NameError)
But I'm using url_helpers successfully throughout the rest of my test script, albeit inside before and it blocks. If I fully qualify the url_helper with Rails.application.routes.url_helpers.signin_path it works, but I'd rather have this work automatically. If I do an include at the top of the script, I get
ActionView::Template::Error:
arguments passed to url_for can't be handled. Please require routes or provide your own implementation
Again, not ideal. I'd rather have this just work. Any thoughts?
Thanks in advance.
Are there no errors if you just did one path? If so, then why not use a RSpec shared_example
require 'rails_helper'
shared_examples 'get_user_in' do |path|
describe "user signs in via #{path}" do
before { visit path }
describe "user enters invalid information" do
before do
fill_in "Email", with: user.email, match: :first
fill_in "Password", with: user.password.reverse, match: :first
click_button "Sign in"
end
it { should have_title("Sign in") }
it { should have_content "Invalid email/password combination!" }
its(:current_path) { should eq "/signin" }
end
end
end
describe "/signin" do
subject { page }
include_examples "get_user_in", 'signin'
end
describe "/signup" do
subject { page }
include_examples "get_user_in", 'signup'
end
Didn't test this out, so there might be some error here.
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
I'm using minitest with factory girl and capybara for integration tests. Capybara works fine when I don't user factory girl to create a user object, like this:
it "logs in a user successfully" do
visit signup_path
fill_in "Email", :with => "joey#ramones.com"
fill_in "Password", :with => "rockawaybeach"
fill_in "Password confirmation", :with => "rockawaybeach"
click_button "Create User"
current_path == "/"
page.text.must_include "Signed up!"
visit login_path
fill_in "Email", :with => "joey#ramones.com"
fill_in "Password", :with => "rockawaybeach"
check "Remember me"
click_button "Log in"
current_path == "/dashboard"
page.text.must_include "Logged in!"
page.text.must_include "Your Dashboard"
end
But as soon as I try to create a user with factory girl weird things start happening, such as the visit method and click_button methods stop working. For instance, there doesn't seem to be anything wrong with this test:
require "test_helper"
describe "Password resets" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "emails user when requesting password reset" do
visit login_path
click_link "password"
fill_in "Email", :with => user.email
click_button "Reset my password"
end
end
And here's my factories.rb:
FactoryGirl.define do
factory :user do |f|
f.sequence(:email) { |n| "foo#{n}#example.com" }
f.password "secret"
f.password_confirmation "secret"
end
end
Here's the actual error that I'm getting:
est_0001_emails user when requesting password reset 0:00:01.624 ERROR
undefined local variable or method `login_path' for #<#<Class:0x007fc2db48d820>:0x007fc2df337e40>
But, visit login_path works fine if I remove #user = FactoryGirl.create(:user)
Is this a bug with Capybara? Or am I doing something wrong here?
I finally got this to work using the DatabaseCleaner gem using DatabaseCleaner.strategy = truncation. Here's what I ended up with:
test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "minitest/autorun"
require "capybara/rails"
require "active_support/testing/setup_and_teardown"
class IntegrationTest < MiniTest::Spec
include Rails.application.routes.url_helpers
include Capybara::DSL
register_spec_type(/integration$/, self)
def last_email
ActionMailer::Base.deliveries.last
end
def reset_email
ActionMailer::Base.deliveries = []
end
end
class HelperTest < MiniTest::Spec
include ActiveSupport::Testing::SetupAndTeardown
include ActionView::TestCase::Behavior
register_spec_type(/Helper$/, self)
end
Turn.config.format = :outline
# Database cleaner.
DatabaseCleaner.strategy = :truncation
factories.rb
FactoryGirl.define do
sequence :email do |n|
"email#{n}#example.com"
end
factory :user do
email
password "secret"
password_confirmation "secret"
end
end
integration/login_integration_test.rb
require "test_helper"
describe "Login integration" do
it "shouldn't allow an invalid login" do
visit login_path
click_button "Log In"
page.text.must_include "invalid"
end
it "should login a valid user" do
DatabaseCleaner.clean
user = FactoryGirl.create(:user)
visit login_path
within ".session" do
fill_in "session_email", :with => user.email
fill_in "session_password", :with => "secret"
end
click_button "Log In"
page.text.must_include "Logged in!"
save_and_open_page
end
end
Remove login_path and try to use url
for example
visit "/users/login" #login_path