using rspec with the default driver is working fine and all tests are passed.
changing driver: :webkit will have a bad side-effect.
step: the user is logged in
step: visiting root_path with a session (current_user)
step: visiting root_path without a session (current_user = nil)
so either after the first visit root_path or before the second, the session is killed or whatever - we can't get the user to stay logged in.
test looks like this
scenario 'something', driver: :webkit do
user = FactoryGirl.create :user
login_as(user)
visit root_path
visit root_path
end
is this a known bug? are there any workarounds or are we missing something ?
as requested:
def login_as(user)
visit root_path
click_on "Login"
fill_in "user[login]", with: user.username
fill_in "user[password]", with: user.password
click_on "Sign in"
end
The default driver runs everything synchronously --- drivers that use real browsers and support javascript do not necessarily do things synchronously - so it's possible in drivers other than rack-test for click_on 'Sign in' to return immediately. Therefore if you're not checking for content that would be seen on success the next visit root_path can get executed immediately and cancel the submission of the login form. To fix that add something like
expect(page).to have_content('You are now logged in') # whatever text is shown on a successful login
as the last line of your login_as method. This is not normally an issue for most people, because after logging in the next step is usually to click on something on the page, which will make Capybara wait for that item to appear thereby waiting for the login to complete.
If that is not what is happening here then the only place (given your sample code) that can be logging the user out is your own app
if you are deleting the session after the first visit, it is expected that the user logout.
Does visiting without session means that visiting the page as anonymous?
Related
I'm having trouble with my test for a method which lets an admin user promote other users to admin by the click of "Promote to Admin". It lies in my controller, I'm writing a feature test for it. I'm using Rails 5.1.4.
def promote
#user = User.find(params[:user_id])
if #user.toggle!(:admin)
flash[:success] = "User is promoted to admin."
redirect_to root_path
else
flash[:notice] = "Can't promote."
redirect_to root_path
end
end
This is the test:
describe "Promotion" do
before do
login_as(User.create!(name: "lala", email: Faker::Internet.email,
password: "lalala", admin: true))
visit users_path
end
context "to admin" do
it "promotes user to admin" do
click_link("Promote to Admin", :match => :first)
expect(current_path).to eq user_promote_path
end
end
end
It gives me the error: Capybara::ElementNotFound:
Unable to find visible link "Promote to Admin"
which I think is because I'm not accessing the right page, trying to log in as admin is perhaps not working.
Any suggestion would be very appreciated!
The most likely reason for your test failing is that you don't appear to have created any other users beyond the one you're logging in as. If you haven't then there wouldn't be any users to show "Promote to Admin" links for. You can always save the page or just do puts page.html to make sure what you think is on the page actually is.
A second issue with your test is that you should never use the eq matcher with current_path. You should be using the Capybara provided path matcher of you want stable tests
expect(page).to have_current_path(user_promote_path)
If you want to be sure that you're on the right page, you can do some debugging with:
save_and_open_page (opens your browser)
save_and_open_screenshot (takes a screenshot and opens it)
If it's all good, maybe Capybara can't find the link : Is it a screen size/responsive issue ? If yes, you can configure the window size that Capybara uses (see this link)
If the test still does not pass, maybe the link is not visible by default ?
You can add to option visible: false in click_link to precise that.
I have the follow test:
def login_user(email, password)
visit new_user_session_path
fill_in 'E-mail', with: email
fill_in 'Password', with: password
click_button 'go'
end
scenario 'some test' do
order = Fabricate(:order_one, company: user.company)
visit "http://127.0.0.1:49645/orders/#{order.id}" # change to visit "/orders/#{order.id}"
login_user(user.email, user.password)
#assert
end
Whats happen is that in first step (visit...) the user is not logged, so I set some informations using cookie. But, when login_user is executed, this cookie is empty.
Usigin selenium-webdriver
Any idea here ?
Thanks in advance
When using a JS capable driver #visit is not guaranteed to have finished loading the page when it returns. Since the cookie is not set in the browser until the response is processed, your login_user is actually telling the browser to stop processing it's current visit and visit somewhere else which stops the cookie from ever being set in the browser. After the first visit you need to wait for something visible on the page to be sure the cookies are correctly set.
scenario 'some test' do
order = Fabricate(:order_one, company: user.company)
visit "http://127.0.0.1:49645/orders/#{order.id}"
expect(page).to have_content('you are not logged in') #or whatever shows on the page visited
login_user(user.email, user.password)
#assert
end
Then /^I should be on the free signup page$/ do
*current_path.should == free_signup_path*
end
Then /^I should be on the free signup page$/ do
*visit free_signup_path*
end
What is the difference between these two ?
#visit tells the browser to go to a new url and will initiate a get request to the app being tested. #current_path lets you read the current location the browser is at.
Note: you should stop using current_path.should == some_path, when verifying the current path, and instead use page.should have_current_path(some_path). The have_current_path matcher includes waiting behavior and will help make tests less flaky with JS capable drivers
You use visit to tell rspec to go to that path to validate if something works or is present. For example:
it 'contains the new sessions form' do
visit 'sessions/new'
current_path.should have_content("Sign In")
end
Granted that my sign in form has a button or whatever with the text 'sign in' this test will pass.
You generally have to declare what path the test hould go to in order to detect content.
Hope this helps!
I am trying to test what happens after logging in through Devise gem. For example, I have the controller to go to student_dashboard_path after users successfully login.
How can I test this with Capybara and Rspec?
I currently have this in:
/spec/features/user_signs_in_sees_dashboard_spec.rb
require 'rails_helper'
feature 'User sign in' do
scenario 'successfully from sign in page and sees student dashboard' do
sign_in
visit student_dashboard_path
expect(current_path).to eq(student_dashboard_path)
end
end
and I have this in:
/spec/support/features/sign_in.rb
module Features
def sign_in
visit user_session_path
fill_in 'Email', with: User.first.email
fill_in 'Password', with: User.first.password
click_button 'Log in'
end
end
and I am getting this error message:
1) User signs in successfully from sign in and sees dashboard
Failure/Error: expect(current_path).to eq(student_dashboard_path)
expected: "/student/dashboard"
got: "/student/login"
I am not sure why I am not able to log in and see the student dashboard.
I'm leaving my original answer below for anyone still on Capybara < 2.5.0 but in 2.5.0 you can now do
expect(page).to have_current_path(<expected path>)
and it will use Capybara's waiting behavior while checking for the path
---- Below is only for Capybara version < 2.5.0
expect(current_path).to eq(...) doesn't wait for the path to change, it just compares to the current path at the time it's called. If you put a sleep after the click button I bet it works. A better solution would be to have something like
expect(page).to have_text('You are now logged in')
after the click_button. That would cause capybara to wait until the log in has completed, the page loads (and the logged in notice appears), and therefore until the current_path has changed too.
Use capybara save_and_open_page in middle to figure out if the fields are properly set. If you work on the same machine you can switch to selenium to test out on the real browser.
Also be sure that this code does not need any JS to work because default capybara matchers will not be able to run it.
I'm trying to run expectations on page.current_url, however, for some reason, page.current_url isn't getting updated.
This happens when I click a link rather than visiting a url. Look:
scenario 'When a user who is not an admin visits the dashboard' do
before do
sign_up user
visit admin_statistics_path
end
its(:current_url){ should have_a_uri_of root_path }
end
All those specs pass. However, when I click a link to admin_statistics_path rather than visiting admin_statistics_path, the current_url expectation fails:
describe 'When a user who is not an admin clicks control panel' do
before do
sign_up user
click_link 'dashboard'
# puts current_url
end
it { should have_css 'div.danger', "You do not have sufficient priviledges to access the admin area. Try logging out and logging in with an account that has admin priviledges." }
it { should have_css 'h1', text: 'Goals'}
its(:current_url){ should have_a_uri_of root_path }
end
All the above expectations pass apart from the current_url one! Even weirder, if I put the current_url, the spec passes:
describe 'When a user who is not an admin clicks control panel' do
before do
sign_up user
click_link 'dashboard'
puts current_url #=> returns the initial url despite the specs passing
end
it { should have_css 'div.danger', "You do not have sufficient priviledges to access the admin area. Try logging out and logging in with an account that has admin priviledges." }
it { should have_css 'h1', text: 'Goals'}
its(:current_url){ should have_a_uri_of root_path }
end
How do I get capybara to wait for a redirect?
Why does putting current_url cause expectations to pass?
Why does visiting a url cause the expectations on the current_url to pass?
Why does clicking a link to a url not cause the expectations on the current_url to pass?
For anyone coming to this question now, Capybara 2.5 added a has_current_path matcher which will use Capybaras waiting behavior when checking the current_path
it { should have_current_path(root_path) }
or
expect(page).to have_current_path(root_path)