For the record, I'm developing with the following:
ruby 1.9.3p327 rails 3.2.12 devise 2.2.3 capybara 1.1.4 rspec 2.13.0
What I wanted was to be able to say:
def before do login_user() endand have it Just Work.
In preparation for Capybara 2.0, I'm putting all my integration tests in the spec/features directory and that created some of the confusion. Code in the spec/integration directory has access to the controller, whereas code in the spec/features directory does not. This means that both HTTP Authentication and Devise login must be handled differently. To resolve this, I started with Matt's approach and modified it so that (so far), it works in any of my tests.
First, I modified Matt's module to add login processing and separate out the HTTP Authentication so that it could be used for controller as well as feature specs. Here's the code:
## spec/support/auth_helper.rb module HTTPHelper def http_config(test_type) @test_type = test_type end def http_login(user,pw) puts "@test_type: #{@test_type}" if @test_type == :controller then request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw) elsif @test_type == :feature then if page.driver.respond_to?(:basic_auth) puts 'Responds to basic_auth' page.driver.basic_auth(user, pw) elsif page.driver.respond_to?(:basic_authorize) puts 'Responds to basic_authorize' page.driver.basic_authorize(user, pw) elsif page.driver.respond_to?(:browser) && page.driver.browser.respond_to?(:basic_authorize) puts 'Responds to browser_basic_authorize' page.driver.browser.basic_authorize(user, pw) elsif page.driver.respond_to?(:browser) && page.driver.respond_to?(:header) encoded_login = ["#{user}:#{pw}"].pack("m*") page.driver.header 'Authorization', "Basic #{encoded_login}" else puts "page.driver.methods: #{page.driver.methods.sort}" if page.driver.respond_to?(:browser) then puts "page.driver.browser methods: #{page.driver.browser.methods.sort}" end raise "I don't know how to log in!" end else raise "I don't know what kind of test this is!" end end end module AuthHelper include HTTPHelper ### For controller specs def login_admin login_user(:admin) end def login_user(user_name=nil) http_config :controller http_login('HTTPname', 'HTTPpassword') if user_name.nil? then @current_user = FactoryGirl.create :user @current_user.confirm! sign_in @current_user else raise NotImplementedError @current_user = FactoryGirl.create :user, :name => :user_name user = User.where(:name => user_name.to_s).first if user.is_a?(Symbol) sign_in user.id end end def current_login User.find(session[:user_id]) end end module AuthRequestHelper include HTTPHelper ### For request, feature & view specs # pass the @env along with your request, eg: # GET '/labels', {}, @env def login_user(user_name=nil) http_config :feature http_login('HTTPname','HTTPpassword') if user_name.nil? then @current_user = FactoryGirl.create :user @current_user.confirm! #Following does not work in feature specs #sign_in @current_user visit ('/') click_on 'Login' fill_in 'Email', with: @current_user.email fill_in 'Password', with: 'password' click_on 'Sign in' else raise NotImplementedError end end end ## Relevant portion of spec/spec_helper.rb ... config.include HTTPHelper config.include AuthRequestHelper, :type => :request config.include AuthRequestHelper, :type => :feature config.include AuthRequestHelper, :type => :view config.include AuthHelper, :type => :controller ... ## spec/support/devise.rb RSpec.configure do |config| config.include Devise::TestHelpers, :type => :controller config.include Devise::TestHelpers, :type => :view endCouple things to note: BothHTTP Authentication and Devise sign_in are different between controller and feature specs primarily, I believe, because of Capybara handling in the feature specs (remember, the controller isn't available in feature specs with Capybara see the "Gotchas" near the bottom of the Capybara Read Me). Also check out this useful post from Thoughtbot.
Before you say anything... yes, I know it's not very DRY: I could have just included the HTTP Authentication code in each of the different authorization modules. But while working on it, I wanted the code isolated for clarity and I'm just happy to have the thing working. Feel free to clean this up if you want.
For now I'm not using the login_admin method, but will be working with that down the line.
Now I can finally get back to the real work of developing my app; hope this helps someone else.
EDIT: Thanks to the folks at thoughbot for this link which describes how to login using Warden. I haven't tried it, but it looks promising.
No comments:
Post a Comment