HTTP basic auth for Capybara
Asked Answered
G

6

35

I'm writing some RSpec tests for my Rails 3 application and trying to switch from Webrat to Capybara. So far so good but the application uses HTTP basic auth to authorize my admin user, any idea how I can test that with Capybara?

Here is my current Webrat step:

it 'should authenticate for admin' do
  basic_auth('user', 'secret')
  visit '/admin'
  response.status.should eql 200
  response.status.should_not eql 401
end

How do I do this with Capybara? Thanks!

Grieco answered 1/12, 2010 at 22:54 Comment(1)
Why would you need to test what response.status IS NOT after you have already determined specifically what it is?Elysia
M
45

I got it to work using page.driver.basic_authorize(name, password) instead

Update:

At the moment, after a Capybara upgrade, I'm using this pile of workarounds:

if page.driver.respond_to?(:basic_auth)
  page.driver.basic_auth(name, password)
elsif page.driver.respond_to?(:basic_authorize)
  page.driver.basic_authorize(name, password)
elsif page.driver.respond_to?(:browser) && page.driver.browser.respond_to?(:basic_authorize)
  page.driver.browser.basic_authorize(name, password)
else
  raise "I don't know how to log in!"
end
Mullane answered 3/12, 2010 at 12:53 Comment(2)
Cool, this works! For future reference: I found a comment by Jonas Nicklas, the creator of Capybara, advising against using the driver as not all possible Capybara drivers support HTTP headers (github.com/jnicklas/capybara/issues/issue/17)... if someone would need this. As far as I am concerned this is the solution I need. Thanks Anders!Grieco
You rock! page.driver.browser.basic_authorize worked for mePupiparous
T
29

The default Capybara driver, rack-test, has a basic_authorize method (with alias authorize) for Basic HTTP Auth, and digest_authorize for Digest HTTP Auth, here you can find them: https://github.com/brynary/rack-test/blob/master/lib/rack/test.rb

So you can do:

page.driver.browser.authorize 'login', 'password'

Or you can write a simple helper for Basic HTTP Auth:

def basic_auth(user, password)
  encoded_login = ["#{user}:#{password}"].pack("m*")
  page.driver.header 'Authorization', "Basic #{encoded_login}"
end
Tristichous answered 29/10, 2011 at 12:36 Comment(4)
Your first line {page.driver.browser.authorize 'login', 'password'} works a treat, shame it won't work in a before :each block for an rspec 2 test but it does work fine directly before to a call to visit. Thanks for the answerWhitener
Worth noting that this method works by adding the Authorization header to the request, so you need to call this BEFORE visit.Colonnade
When I try to use the first option, I get an error: undefined method `authorize' for #<Capybara::Poltergeist::Browser:0x007fae7c592540> (NoMethodError) has anyone seen anything similar?Honorarium
@Szymon I tried page.driver.browser.authorize 'login', 'password' and it works fine but now the rest of examples in my spec file just fail and if I move the one with the basic auth to the bottom they all pass. Is there anything to clean up on the driver after using it on the required pages only?Fallon
P
7

None of the page.driver.* solutions worked for me. I'm using Poltergeist, not Selenium, so that might have something to do with it. Here's what did work:

RSpec.shared_context "When authenticated" do
  before do
    username = 'yourusername'
    password = 'yourpassword'
    visit "http://#{username}:#{password}@#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}/"
  end
end

Then, in your spec:

feature "Your feature", js: true do
  include_context "When authenticated"

  # Your test code here...
end
Prow answered 17/4, 2015 at 15:18 Comment(1)
This is easy and clean. Instead of a shared_context, I put in the config.before(:each) do block.Nightstick
D
2

This has changed in recent versions of cucumber-rails (I am using 1.0.2).

cucumber-rails uses the Rack/Test driver by default, so if you have not changed that, the following instructions will work.

Create features/step_definitions/authorize.rb:

Given /^I am logged in as "([^\"]*)" with "([^\"]*)"$/ do |username, password|
  authorize username, password
end

Now you can use this in your features:

Given I am logged in as "admin" with "password"
Dosi answered 2/8, 2011 at 9:36 Comment(2)
Thanks Joost but the question didn't mention Cucumber. It was a Capybara only question. Good to know there is a simple way in Cucumber to do this though.Grieco
Regardless if you use RSpec and not Cucumber to run the features, the answer is equally valid if you have kept the default driver in Capybara which is Rack::Test.Dosi
R
1

I had to do this horrible hack to get it work worth headless and with javascript

Given /^I am logged in$/ do
 if page.driver.respond_to?(:basic_authorize)
   page.driver.basic_authorize('admin', 'password')
 else
   # FIXME for this to work you need to add pref("network.http.phishy-userpass-length", 255); to /Applications/Firefox.app/Contents/MacOS/defaults/pref/firefox.js
   page.driver.visit('/')
   page.driver.visit("http://admin:password@#{page.driver.current_url.gsub(/^http\:\/\//, '')}")
 end
end
Raptorial answered 13/12, 2010 at 12:42 Comment(0)
K
0

Man, none of these solutions worked for me.

Pistos' solution came close and worked for feature specs with js: true but failed when headless.

This below solution works for me for both headless and js: true specs.

spec/support/when_authenticated.rb

RSpec.shared_context 'When authenticated' do
  background do
    authenticate
  end

  def authenticate
    if page.driver.browser.respond_to?(:authorize)
      # When headless
      page.driver.browser.authorize(username, password)
    else
      # When javascript test
      visit "http://#{username}:#{password}@#{host}:#{port}/"     
     end
  end

  def username
    # Your value here. Replace with string or config location
    Rails.application.secrets.http_auth_username
  end

  def password
    # Your value here. Replace with string or config location
    Rails.application.secrets.http_auth_password
  end

  def host
    Capybara.current_session.server.host
  end

  def port
    Capybara.current_session.server.port
  end
end

Then, in your spec:

feature 'User does something' do
  include_context 'When authenticated'

  # test examples
end
Kreutzer answered 8/9, 2015 at 23:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.