I am using Rails 3.1.0 and the recaptcha gem from here. I was running a cucumber test that checks that users can sign up. Sign up requires users to fill out the captcha. I know that my test does not touch the captcha:
When /^I create a new account with email: "(.*?)" and password: "(.*?)"$/ do |email, pw|
click_link "Sign up"
fill_in "Email", :with => email
fill_in "Password", :with => pw
fill_in "Password confirmation", :with => pw
click_button "Sign up"
end
But the test still passes. I check success by verifying that the successful sign up message is present on the page and the recaptcha failure message is not present using this step:
Then /^I should (not )?see "(.*)"$/ do |negate, text|
if negate
page.should_not have_content text
else
page.should have_content text
end
end
The controller is almost identical to the first one suggested here.
class RegistrationsController < Devise::RegistrationsController
def create
if verify_recaptcha
super
else
build_resource
clean_up_passwords(resource)
flash.now[:alert] = "There was an error with the recaptcha code below. Please re-enter the code."
flash.delete :recaptcha_error
render :new
end
end
end
Is there any reason why recaptcha would not work in the test environment? It seems to work fine in development.
Recaptcha by default does not verify the captcha in the test and cucumber environments (see verify logic, configuration logic, and default value ). If it weren't for this, either testing would be very difficult or the captcha wouldn't be very useful.
Just to add to Bens answer and give a potential workaround:
To make sure things don't work (it "fails with recaptcha") in RSpec I've done
before do
Recaptcha.configuration.skip_verify_env.delete('test')
end
after do
Recaptcha.configuration.skip_verify_env << 'test'
end
Thanks Ben
Related
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
I'm trying to test devise sign in, sign out and all the other scenarios, however I cannot get a single scenario to past, lets take login failure
in my feature I have
scenario 'user cannot sign in if not registered' do
login_as('user2#example.com', 'meow')
visit overviews_path
save_and_open_page
expect(page).to have_content I18n.t 'devise.failure.not_found_in_database', authentication_keys: 'email'
end
I also have the sign_in helper setup as;
def sign_in(email, password)
user.confirm!
visit new_user_session_path
fill_in 'Email', with: email
fill_in 'password', with: password
click_button 'Log in'
end
however this create an error;
expected to find text "Invalid email or password." in "TypeError at /overviews ======================= > no implicit conversion of Symbol into Integer spec/features/users/sign_in_spec.rb, line 14
any ideas?
You named your helper method sign_in but you're calling login_as in your scenario. You should use one approach or the other, not both.
UPDATE: OK, rechecked the documentation, and you should either use your own helper so that you're emulating an actual user signing in, or the login_as provided by Warden, in which case make sure you're included this in your tests / rspec setup:
include Warden::Test::Helpers
Warden.test_mode!
On a side note, you should confirm your user in your factory/fixture, not in your helper method (where it's probably not defined).
I have a rails app that includes a blog feature.
In one single feature test (using Capybara::Rails::TestCase, with ruby tests (asserts/refutes) i.e, not spec) for the blog, I want to test adding a post, adding comments, editing the post, etc. as individual tests - each of these tests builds upon the last one, as the post created in the first test is commented on in the second test, and so on.
I have seen posts which show workarounds for doing this in a unit test (global variables, use setup/teardown), but I wondered if there is a more direct way to do it in a feature test, since it is likely more common here.
Ideally, I want the login session to persist, as well as database records created in previous tests to persist across each test in the TestCase. Setup and teardown could be used to login each time, but not the intermediate records created for posts, comments, etc.
I want something like:
class BlogTest< Capybara::Rails::TestCase
test 'can sign in' do
user = User.create!(name: "user",
email: "user#example.com",
password: "passw0rd!", password_confirmation: "passw0rd!")
visit new_user_session_path
fill_in('Login', :with => user.email)
fill_in('Password', :with => user.password)
check('Remember me')
click_button('Sign in')
end
test 'can create post' do
visit new_post_path # how can I have user logged in?
fill_in "Title", with: "My first post title!"
fill_in "Body", with: "My first post body!"
click_button "Publish"
end
test 'can comment on post' do
visit post_path(Post.first) # should go to post created in last test
click_button "Add comment"
...
end
end
I have heard that this may be possible in Cucumber, but chose not to use Cucumber for other reasons, so want it to work with Minitest and Capybara.
Capybara::Rails::TestCase inherits from ActiveSupport::TestCase. One of ActiveSupport::TestCase's main features is that it runs each test in a database transaction. There are ways to work around this, but I would not recommend them.
Instead, I suggest you work with the behavior of the rails test classes. In this case, you want to share actions between tests. I recommend you extract those actions into methods and call those methods in your tests. Here is how I would implement this with your test code:
class BlogTest< Capybara::Rails::TestCase
def user
#user ||= User.create!(name: "user",
email: "user#example.com",
password: user_password,
password_confirmation: user_password)
end
def user_password
"passw0rd!"
end
def sign_in(email, password)
visit new_user_session_path
fill_in('Login', :with => email)
fill_in('Password', :with => password)
check('Remember me')
click_button('Sign in')
end
def create_post(title = "My first post title!",
body = "My first post body!")
visit new_post_path # how can I have user logged in?
fill_in "Title", with: title
fill_in "Body", with: body
click_button "Publish"
end
def comment_on_post(post, comment)
visit post_path(post)
click_button "Add comment"
# ...
end
test "can sign in" do
sign_in(user.email, user_password)
# add assertions here that you are signed in correctly
end
test "can't sign in with a bad password" do
sign_in(user.email, "Not the real password")
# add assertions here that you are not signed in
end
test "can create post when signed in" do
sign_in(user.email, user_password)
create_post
# add assertions here that post was created correctly
end
test "can't create post when not signed in" do
create_post
# add assertions here that post was not created
end
test "can comment on post when signed in" do
sign_in(user.email, user_password)
create_post
post = user.posts.order(:created_at).last
comment_on_post(post, "I can comment because I'm signed in!")
# add assertions here that comment was created correctly
end
test "can't comment on post when not signed in" do
post = Post.first
comment_on_post(post, "I can't comment because I'm not signed in!")
# add assertions here that comment was not created
end
end
Each action has a good name, and you can reuse those actions for different types of tests. Each test is executed within a database transaction, so each time the each test method is run the database looks the same.
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
I'm trying to write integration tests with rspec, factory_girl & capybara. I also have cucumber installed, but I'm not using it (to my knowledge).
I basically want to prepopulate the db with my user, then go to my home page and try to log in. It should redirect to user_path(#user).
However, sessions don't seem to be persisted in my /rspec/requests/ integration tests.
My spec: /rspec/requests/users_spec.rb
require 'spec_helper'
describe "User flow" do
before(:each) do
#user = Factory(:user)
end
it "should login user" do
visit("/index")
fill_in :email, :with => #user.email
fill_in :password, :with => #user.password
click_button "Login"
assert current_path == user_path(#user)
end
end
Returns:
Failures:
1) User flow should login user
Failure/Error: assert current_path == user_path(#user)
<false> is not true.
# (eval):2:in `send'
# (eval):2:in `assert'
# ./spec/requests/users_spec.rb:16
Instead, it redirects to my please_login_path - which should happen if the login fails for any reason (or if session[:user_id] is not set).
If I try to put session.inspect, it fails as a nil object.
If I try to do this in the controller tests (/rspec/controllers/sessions_spec.rb), I can access the session with no problem, and I can call session[:user_id]
If you are using Devise, you'll need to include Warden::Test::Helpers (right after the require of spec_helper is a good place) as outlined in the warden wiki.
The call to session is returning nil because capybara doesn't provide access to it when running as an integration test.
I have the same problems and although filling out a form might be an option for some, I had to roll my own authentication ruby because I was using a third party auth system (Janrain to be exact).... in my tests I ended up using something like this:
Here is what I have in my spec/support/test_helpers_and_stuff.rb
module AuthTestHelper
class SessionBackdoorController < ::ApplicationController
def create
sign_in User.find(params[:user_id])
head :ok
end
end
begin
_routes = Rails.application.routes
_routes.disable_clear_and_finalize = true
_routes.clear!
Rails.application.routes_reloader.paths.each{ |path| load(path) }
_routes.draw do
# here you can add any route you want
match "/test_login_backdoor", to: "session_backdoor#create"
end
ActiveSupport.on_load(:action_controller) { _routes.finalize! }
ensure
_routes.disable_clear_and_finalize = false
end
def request_signin_as(user)
visit "/test_login_backdoor?user_id=#{user.id}"
end
def signin_as(user)
session[:session_user] = user.id
end
end
Then in my request spec, with capybara and selenium, I did the following:
describe "Giveaway Promotion" do
context "Story: A fan participates in a giveaway", js: :selenium do
context "as a signed in user" do
before :each do
#user = Factory(:user)
request_signin_as #user
end
it "should be able to participate as an already signed in user" do
visit giveaway_path
....
end
end
end
end
BTW, I came up with solutions after trying the proposed solutions to this post and this post and neither of them worked for me. (but they certainly inspired my solution)
Good luck!
You've probably moved on from this, but I was just struggling with the same question. Turns out it was a matter of syntax. I was using symbols for :email and :password and I should've been using strings instead ("email" and "password").
In other words, try changing this:
fill_in :email, :with => #user.email
fill_in :password, :with => #user.password
to this:
fill_in "email", :with => #user.email
fill_in "password", :with => #user.password