Rails - Losing session with Integration Tests and Capybara - CSRF related?

寵の児 提交于 2019-11-30 04:58:21

I'm new to capybara too and I was having a similar problem.

I was trying to login a user doing something like this:

post user_session_path, :user => {:email => user.email, :password => 'superpassword'}

And that was working ok until I tried to do something with capybara, such as visiting a page and just testing if the user was logged in. This simple test was not passing:

visit root_path
page.should have_content("logout") #if the user is logged in then the logout link should be present

At first I thought capybara was clearing the sessions but I was wrong. The thing that took me some time to realize is that the driver capybara is using handles its own sessions, so, from the point of view of capybara my user was never logged in. To do so you have to do it like this

page.driver.post user_session_path, :user => {:email => user.email, :password => 'superpassword'}

Not sure if this is your case, but hope that helps.

The manual way of doing it is very simple:

it "does something after login" do
  password = "secretpass"
  user = Factory(:user, :password => password)
  visit login_path
  fill_in "Email", :with => user.email
  fill_in "Password", :with => password
  click_button "Log in"
  visit # somewhere else and do something
end

You can then break this out into a function in your 'spec_helper.rb':

# at the bottom of 'spec_helper.rb'
def make_user_and_login
  password = "secretpass"
  @user = Factory(:user, :password => password)
  visit login_path
  fill_in "Email", :with => @user.email
  fill_in "Password", :with => password
  click_button "Log in"
end

and use it in any of your tests (probably request specs):

it "does something after login" do
  make_user_and_login
  # now test something that requires a logged in user
  # you have access to the @user instance variable
end

I was able to fix this error by setting this value to true in config/initializers/test.rb

# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = true

Beforehand, the CSRF <meta> tags were not printing out to the <head>. After changing this value they finally appear.

This might be a long shot but I believe we end up in a bad state after click_button 'Sign in' and calling visit elsewhere immediately after.

My theory is that when we click the button the request hasn't completed yet, and we kill it by visiting another path.

From the Capybara documentation:

When issuing instructions to the DSL such as:

click_link('foo') click_link('bar') expect(page).to have_content('baz')

If clicking on the foo link triggers an asynchronous process, such as an Ajax request, which, when complete will add the bar link to the page, clicking on the bar link would be expected to fail, since that link doesn't exist yet. However Capybara is smart enough to retry finding the link for a brief period of time before giving up and throwing an error.

If this is the case the solution is simple: give Capybara something to look for and let it wait until the request is complete. This can be as simple as adding:

expect(page).to have_text('Signed in as bob@example.com')

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!