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?
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With