Capybara loses cookies between requests - ruby-on-rails

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

Related

Rspec + Capybara - Paypal Express Sandbox checkout timing

My test setup is Rspec + Capybara with Poltergeist / Phantomjs.
I'm trying to test the Paypal express sandbox - sometimes the tests pass sometimes not. Looks like a timing problem.
Here's my test snippet which redirects to Paypal and on success back to my page. I'm using my credit on the sandbox account so no creditcard processing...
Anyone has a working setup for that or any suggestions?
# confirm order and proceed / redirect to paypal
find('button#booking-button').click
using_wait_time(60) do
# login on paypal express sandbox page
within_frame(find('iframe')) do
fill_in 'email', with: Rails.application.secrets.paypal_test_user_email
fill_in 'password', with: Rails.application.secrets.paypal_test_user_password
find('button#btnLogin').click
end
using_wait_time(60) do
# confirm payment and redirect back to my page
find('input#confirmButtonTop').click
using_wait_time(60) do
# check if element on my page exists
expect(page).to have_selector('div#checkout-thank-you')
end
end
end
Jack Kinsella's answer served us very well for 2 years (🙏🏼), but PayPal recently updated their UI in Europe.
You could log in as a guest, but test credit cards are a pain as they keep expiring and filling out the form takes much longer. You'll now first have to accept cookies and indicate that you want to log in.
def pay_with_paypal
paypal_window =
window_opened_by do
within_frame(find(".paypal-buttons iframe")) do
first(".paypal-button").click
end
end
within_window(paypal_window) do
# similar, just couple of extra steps and calling reusable method for details
Capybara.using_wait_time(5) do
# accept cookies
click_button("Accept Cookies") if page.has_button?("Accept Cookies")
# switch to login form (if needed)
click_on("Log In") if page.has_content?("PayPal Guest Checkout")
add_paypal_login_details
click_on("Pay Now")
end
end
end
def add_paypal_login_details
fill_in "login_email", with: "youraccount#test.com"
click_on "Next"
fill_in "login_password", with: "yourpassword"
find("#btnLogin").click
end
I've found that PayPal can shift quite a bit depending on region and whether or not you're running headless Chrome. Using save_and_open_screenshot could help determine your exact scenario.
It depends on what API you are using.
For the REST API (v1), the following worked for me:
# click client side checkout button
paypal_window =
window_opened_by do
within_frame(find('.paypal-button iframe')) do
find('.paypal-button-text').click
end
end
# pay within popup window
within_window(paypal_window) do
click_link 'Log In'
# I am assuming you have a buyer email/password in the sandbox set up
fill_in 'login_email', with: MyConfig['paypal_buyer_email']
click_on 'Next'
fill_in 'password', with: MyConfig['paypal_buyer_password']
click_on 'Log In'
click_on 'Pay Now'
end
At time of writing (for the old SOAP API not the newer REST API), this works:
click_link 'Log In'
using_wait_time(60) do
fill_in 'login_email', with: 'email'
click_on 'btnNext'
fill_in 'password', with: 'password'
click_on 'btnLogin'
click_on 'confirmButtonTop'
end
looks like they changed their layout. form instead of iframe. here's whats working for me some times:
find('button#booking-button').click
using_wait_time(60) do
within('form.proceed.maskable') do
fill_in 'email', with: Rails.application.secrets.paypal_test_user_email
fill_in 'password', with: Rails.application.secrets.paypal_test_user_password
find('button#btnLogin').click
end
using_wait_time(60) do
find('input#confirmButtonTop').click
using_wait_time(60) do
expect(page).to have_selector('div#checkout-thank-you')
end
end
end

Authlogic session in Capybara doesn't exist immediately

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'

How to check page content after button click correctly?

I want to check if there loads correct page after clicking 'sign in' button.
users_spec.rb
before :each do
user = User.create(:email => 'admin#cm.com', :password => '123')
end
it "signs me in" do
visit '/users/sign_in'
within("#new_user") do
fill_in 'Email', :with => 'admin#cm.com'
fill_in 'Password', :with => '123'
click_button 'Sign in'
end
expect(page).to have_content '#{user.name}'
end
expect(page).to have_content 'Dashboard' checks is there user.name word on the same page, where the form is located. So, what's the sense in click_button then? How to make it check content on the page that should load AFTER click_button? By the way, how to correctly name such tests?
Sorry, if it's a silly question, I'm a newbie in rspec :c
Thank you!
You have to pick some content that appears on the page after the user logs in. Does your app display a message saying something like "You are now logged in", if so you can do
expect(page).to have_content("You are now logged in")
If not, does it display the users name in a header bar? Then you can do something like
expect(page).to have_css("header", text: user.name)
etc... The key is that whatever you're searching for needs to appear on the next page but not on the page with the form. In both of those cases Capybara will wait up to Capybara.default_max_wait_time seconds while retrying to find the text (assuming you're using a driver other than rack-test) which should give the next page time to load. If you're using the rack-test driver then there is no JS or asynchronous support, the click_button should have submitted a form and the expect won't execute until the next page has loaded.
As for test naming -- name it something that makes sense to you so you know what its doing a year from now.

rspec + webkit is losing session/cookie after first request

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?

"background(:all)" block doesn't work in rpsec feature spec

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

Resources