Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails, Capybara and subdomains: how to visit certain subdomain

Rails 3, Cucumber 0.9.4, Capybara 0.4.0

I want to test my features with subdomain. I found that solution:

Given /^I visit subdomain "(.+)"$/ do |sub|
  Capybara.default_host = "#{sub}.example.com" #for Rack::Test
  Capybara.app_host = "http://#{sub}.example.com:9887" if Capybara.current_driver == :culerity
end

It works if I run cucumber features/subdomain.feature but it fails if I run cucumber features! It's unbelievable, but it's true. I logged current urls and it is subdomain.example.com for cucumber features/subdomain.feature and www.example.com for cucumber features for one scenario with

Scenario: subdomain scenario
  Given I visit subdomain "subdomain"

in both cases!

I don't know the reason...

Is there best way for testing subdomains with capybara?

like image 576
petRUShka Avatar asked Nov 18 '10 17:11

petRUShka


3 Answers

Okay, here is what should be a fairly straightforward and easy to understand hack of Capybara that yields the desired behavior, namely to be able to create a new session each time you switch subdomains. This is useful for sites where a user registers on one domain (which results in a subdomain being created for his account) and then ends up needing to navigate over to that subdomain.

First of all (and this part is fairly common to the other solutions out there) go ahead and give yourself a way to change Capybara.default_host in a Cucumber step. I did it like this:

Then /^I switch the subdomain to (\w+)$/ do |s|
  Capybara.default_host = "#{s}.smackaho.st"
end

Stick this step into your Cucumber feature at the point where you want the new subdomain to be used. For example:

When I open the email
Then I should see "http://acme.rightbonus.com/users/confirmation" in the email body

Given I switch the subdomain to acme
When I follow "Click here to finish setting up your account" in the email
Then I should be on the user confirmation page for acme

Now for the magical monkeypatching that makes this work. Basically, you want Capybara to be smart enough to detect when the subdomain has changed and reset its RackTest session object.

# features/support/capybara.rb

class Capybara::Driver::RackTest
  # keep track of the default host you started with
  def initialize(app)
    raise ArgumentError,
      "rack-test requires a rack application, but none was given" unless app
    @app = app
    @default_host = Capybara.default_host
  end

  def process(method, path, attributes = {})
    reset_if_host_has_changed
    path = ["http://", @default_host, path].join
    return if path.gsub(/^#{request_path}/, '') =~ /^#/
    path = request_path + path if path =~ /^\?/
    send(method, to_binary(path), to_binary( attributes ), env)
    follow_redirects!
  end

  private

  def build_rack_mock_session # :nodoc:
    puts "building a new Rack::MockSession for " + Capybara.default_host
    Rack::MockSession.new(app, Capybara.default_host || "www.example.com")
  end

  def reset_if_host_has_changed
    if @default_host != Capybara.default_host
      reset! # clears the existing MockSession
      @default_host = Capybara.default_host
    end
  end
end

This patch works with Capybara 0.4.1.1 and will probably not work with different versions unless modified. Good luck.

like image 188
Obie Avatar answered Oct 16 '22 10:10

Obie


Had the same problem for a bit with and my test had to sometimes switch or redirect back and forth between subdomains.

given this step:

When /^(?:|I )go to "(.+)"$/ do |url|
  visit url
end

When I go to "http://mysubdomain.example.org" works in rack test, but if you are redirected by the app or follow a link to some other path the host reverts to the default_host.

There's a fork of rack-test by hassox that ensures that rack-test keeps track of the host used in the previous request.

So, in my Gemfile, before requiring capybara:

  gem 'rack-test', :git => "https://github.com/hassox/rack-test.git"

Now if I need to hit a particular subdomain (or the app redirects me to one, like secure.myapp.com/sign_in) I'm sure that the feature can read more like a browser would behave: it will allow me to stay on the current subdomain until I go–using capybara's visit("somesubdomain.example.org")–or am redirected by the app to a different host.

like image 44
ilpoldo Avatar answered Oct 16 '22 10:10

ilpoldo


If your needs don't include anything to do with sessions, and all you're after is visiting a different subdomain, I wrote this function:

def visit_with_subdomain(uri, options = {})
  domain = Capybara.default_host
  port = Capybara.server_port
  subdomain = options[:subdomain]
  visit "http://#{subdomain}.#{domain}:#{port}#{uri}"
end

You can call it like this:

visit_with_subdomain some_path, subdomain: some_subdomain
like image 42
Jason Swett Avatar answered Oct 16 '22 12:10

Jason Swett