Why is Capybara discarding my session after one event?
Asked Answered
Y

1

20

Testing a rails application which has before_filter :authenticate_user! for most controllers, I cannot get Capybara to preserve a session.

I have Capybara configured using PhantomJS with poltergeist.

I use the following helper:

require 'spec_helper'
include Warden::Test::Helpers

module FeatureHelpers
  def login(user = FactoryGirl.create(:default_user))
    login_as user, scope: :user
    user
  end
end

I have the following spec file:

require 'spec_helper'
include Warden::Test::Helpers

feature 'Leads Data Tasks View' do
  before(:each) do
    @user = login
  end

  after{ Warden.test_reset! }

  context "clicking a task button" do
    scenario "login persists across multuple actions", js: true do
      visit '/tasks'

      page.should have_selector('#parse', count: 1)
    end
  end
end

When I run the test as it's shown here, it will pass. However, if I invoke a click_link on something that performs AJAX actions, or if I simply try to do visit '/tasks' twice, the should assertion will fail because I'll get redirected to the login page of the app.

I've tried a few different approaches, including setting up a Capybara::Session, but I still get 401 codes on AJAX requests and I can only successfully visit once per spec.

What am I doing wrong?

Yellow answered 4/9, 2013 at 20:59 Comment(9)
Based on the syntax your using, im guessing this is devise. Are you firing many ajax calls? Devise has some odd behaviour when dealing with ajax calls. See #11846000 for more infoOrangutan
Thank you for pointing me to that question. To answer your question, I am often firing many AJAX calls, however this issue appears to be happening even if I don't fire any. If I duplicate the visit call so that it simply loads the page twice, no AJAX happens in between.Yellow
Additionally, for the sake of testing I decided to try both option 2 from the accepted answer in your linked question, as well as temporarily commenting out protect_from_forgery in my application controller. Neither of these changed the outcome; I still get 401 on all but the first request.Yellow
Does this work if it was ran normally. i.e. A user using it?Orangutan
Yes, it works in all environments except automated tests (PhantomJS is headless, so it isn't launching Chrome/Firefox instances)Yellow
Not sure if this will make a difference, but i use Devise::Test::Helpers instead of Warden::Test::HelpersOrangutan
According to github.com/plataformatec/devise/issues/1114, it's not recommended to use Devise::TestHelpers outside of controller specs. I tried swapping one out for the other and I got a pile of errors on my controller specs.Yellow
Not sure if this will help but try forcing phantom js to use the same db connection. Have a look at this railscasts: railscasts.com/episodes/391-testing-javascript-with-phantomjs and try the code in spec/support/shared_db_connection.rbMidpoint
This suggestion solved the problem! Would you mind writing up an answer that summarizes the solution so that I can accept it and give you the bounty?Yellow
M
24

So the problem was that phantomjs driver (poltergeist) was using a separated database connection. I had the exact same issue before and I got a solution from railscast episode 391 with the code in spec/support/shared_db_connection.rb

Since I can't find license for his code so I will just link to the code here

Midpoint answered 17/9, 2013 at 10:0 Comment(3)
I suspect you use DatabaseCleaner gem, and you should tell it to use :truncation strategy explicitly for capybara tests. Also self.use_transactional_fixtures = false. This way you dont need to share connection.Mapel
@Mapel Please remember that :truncation will most likely be very slow as actual DB writes and subsequent deletion will occur. Shared connection allows running specs in transactions, which is much faster. Try to make shared connection work, it's absolutely worth it. See also EvilMartian's TestProf gem, which provides the shared connection backport.Sari
That was not random answer but my experience. Capybara runs additional thread, that's why you can't just make it shared connection, and that's why you use truncation which is shared for everything unlike transaction. If you know how to defeat capybara with spinning up another thread and share the connection - let me know. Untill then that's probably the only solution.Mapel

© 2022 - 2024 — McMap. All rights reserved.