I am trying to test a signup flow using rspec and cpaybara within a feature spec. The signup happens via ajax and capybara doesn't wait for the request to complete. I am using the capybara matchers as specified in the documentation. If I add a sleep statement the test succeeds
let(:user) { create(:user, email: 'test#example.com', password: 'password') }
scenario 'visitor can sign up with valid email address and password', js: true do
sign_up_with('Name', 'test#example.com', 'password')
expect(page).to have_content(I18n.t( 'devise.registrations.signed_up'))
end
Helper Method to handle signup:
def sign_up_with(name, email, password)
visit root_path
find(:xpath,"//a[text()='Join']").click
within("#sign_up_choices") do
click_on "Sign up with Email"
end
within("#sign_up") do
fill_in 'user[name]', with: name
fill_in 'user[email]', with: email
fill_in 'user[password]', with: password
click_button 'Sign up'
end
end
When the user clicks on signup the form gets submitted via ajax.
Error I am getting occasionally:
Selenium::WebDriver::Error::StaleElementReferenceError:
stale element reference: element is not attached to the page document
(Session info: chrome=55.0.2883.95)
(Driver info: chromedriver=2.25.426935 (820a95b0b81d33e42712f9198c215f703412e1a1),platform=Mac OS X 10.11.4 x86_64)
Any help would be much appreciated!
Related
My spec:
let(:user) { FactoryGirl.create(:user) }
context 'When user is logged in' do
scenario 'Log in' do
visit login_path
within '.new_user_session' do
fill_in 'username', with: user.email
fill_in 'password', with: user.password
click_on 'Log in'
end
visit new_search_path
expect(page).to have_text "Welcome #{user.name}!"
end
end
The issue is that when visiting new_search_path, even though the login in successful, the page behaves as if there is no user logged in. If I add a sleep(1) call right before visit new_search_path, everything works fine.
Anyone know why this is happening?
I'm using authlogic, capybara, and selenium.
The action triggered.by click_on can occur asynchronously. Therefore, if you do a visit immediately after it can cause the login request to abort and the session cookies never get set. To solve that you need to check for page text/content that indicates the login has succeeded. Something like
expect(page).to have_text 'You are now logged in'
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.
I'm trying to test the devise feature for reseting a password, but I'm having some issues trying to test the link in the mail and visiting the page that is linked to.
I've tried unsuccessfully 2 ways to do it:
1)
scenario "User resets his password" do
user2 = FactoryGirl.create(
:user, reset_password_token: "new_password",
)
visit new_user_password_path
fill_in "Email", with: user2.email
click_on "Send me reset password instructions"
open_email(user2.email)
click_first_link_in_email
expect(page).to have_content("Reset your password")
end
If I do this I get the error:
Failure/Error: click_first_link_in_email
ActionController::RoutingError:
No route matches [GET] "/1999/xhtml'"
2)
scenario "User resets his password" do
user2 = FactoryGirl.create( :user, reset_password_token: "new_password" )
visit edit_user_password_path(reset_password_token:
expect(page).to have_content("Reset your password")
end
If I do this it takes me to the initial page saying:
"You can't access this page without coming from a password reset email"
Solved!
there were more links inside the mail.
The way that I selected the one that I wanted was by using links_in_email(email).
I used pry to check the links inside the array (links_in_email(email) and then select the one that I was interested in.
scenario "User resets his password" do
visit new_user_password_path
fill_in "Email", with: #user.email
click_on "Send me reset password instructions"
mail = ActionMailer::Base.deliveries.last
link = links_in_email(mail)[1]
visit link
expect(page).to have_content("Reset your password")
end
Ok, I'm using has_secure_password in my User model to automatically fill in the :password_digest field in my model.
I'd like to simulate a login in my test_account_page.rb integration test below, so that I can confirm that a logged in user can access the page at account_path.
test "a logged in user can access their account page" do
#user = FactoryGirl.create(:user)
# Sign in first
visit signin_path
fill_in 'email', with: #user.email
fill_in 'password', with #user.password # <!-- this won't work
click_button 'Login'
# Then visit account page
...
end
This won't work because #user.password does not give us the password in plain text form (the whole idea of using has_secure_password in the first place is to make the original password unrecoverable).
So how do I test a page hidden behind the login screen when using has_secure_password?
Try to use user attributes like this:
test "a logged in user can access their account page" do
#user = FactoryGirl.create(:user)
#user_attrs = FactoryGirl.attributes_for(:user)
# Sign in first
visit signin_path
fill_in 'email', with: #user_attrs.email
fill_in 'password', with #user_attrs.password
click_button 'Login'
# Then visit account page
...
end
I'm using rspec, capybara and Selenium to test my whole application stack. I've turned off transactional fixtures, and I'm using database cleaner to clean my database only after the whole suite has been run. These allows me to test things based using objects created in preceding tests.
Anyway, let's say I want to create user a999 (via a form, so a test in itself) and then proceed to test logging him out and logging him back in.
def sign_up(first_name, last_name, profile_name, email, password)
visit "/"
click_link "Register"
fill_in('First name', with: first_name)
fill_in('Last name', with: last_name)
fill_in('Profile name', with: profile_name)
fill_in('Email', with: email)
fill_in('Password', with: password)
fill_in('Password confirmation', with: password)
click_button 'Sign up'
end
feature "user a999 sign up", js: true do
before(:each){
sign_up( #a999.first_name, #a999.last_name, #a999.profile_name, #a999.email, #a999.password )
}
scenario "welcome message" do
expect(page).to have_content ("Welcome," + #a999.first_name)
end
scenario "can log out" do
end
scenario "can log in" do
end
end
The code above almost works. This is what happens when it's run:
The before block signs up the user before the "welcome message" expectation (I see it physically happening in Firefox thanks to Selenium), and then the welcome message appears after a redirect so the "welcome message" spec passes.
However, because I have the before block set to 'each' the before block is run another two times, meaning I now have three a999 users in the database.
Of course, and setting the before block to (:all) should fix this problem. The user is signed up one, and we go from there, signing the exact same user in and out. It's a feature test that tests the whole stack remember, so I want to do this properly, emulate how a real user will be using my app.
def sign_up(first_name, last_name, profile_name, email, password)
visit "/"
click_link "Register"
fill_in('First name', with: first_name)
fill_in('Last name', with: last_name)
fill_in('Profile name', with: profile_name)
fill_in('Email', with: email)
fill_in('Password', with: password)
fill_in('Password confirmation', with: password)
click_button 'Sign up'
end
feature "user a999 sign up", js: true do
before(:all){
sign_up( #a999.first_name, #a999.last_name, #a999.profile_name, #a999.email, #a999.password )
}
scenario "welcome message" do
expect(page).to have_content ("Welcome," + #a999.first_name)
end
scenario "can log out" do
end
scenario "can log in" do
end
end
But with this code nothing happens at all. Seriously, just nothing. Selenium doesn't follow the code in the before block at all! Firefox doesn't even start up.
Why is this? I mean, that should work at the very least.
before(:each) = signs user up before my eyes
before(:all) = completely dead
I can't explain why nothing comes up at all, but based on numerous posts*, you can't reasonable use before(:all) with capybara, since it resets the session between each example.
*Related posts:
Capybara and before(:all) in rspec
capybara/selenium with rspec before :all hook