Rails, Capybara and subdomains: how to visit certain subdomain - ruby-on-rails-3

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.

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

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.

Related

How to use StaticLiveServerTestCase with different domains?

I am using selenium for functional tests with geckodriver and firefox.
I see the host is http://localhost:62305 and this is generated in that class with:
#classproperty
def live_server_url(cls):
return 'http://%s:%s' % (cls.host, cls.server_thread.port)
In the functionality I am creating, I give the user a way to create a tenant with its own subdomain but for the purpose of the unit test it can be a domain.
So for example I create a tenant with domain example.com, how can I get
StaticLiveServerTestCase to point that domain name to the currently running app on that same port, within the same functional test method.
I looked at this post suggesting editing /etc/hosts. The problem with that is it won't just work on CI and other people's computers.
You can do this.
class SeleniumTests(StaticLiveServerTestCase):
#classmethod
def setUpClass(cls):
cls.host = 'host.com'
cls.port = 3000
super(SeleniumTests, cls).setUpClass()
....
in your test class and once you do this for instance
self.selenium.get('%s%s' % (self.live_server_url), 'some_url')
your browser gonna be launch with the right URL like this
http://host.com:3000/some_url/,
notice how the super call is made after setup the port and host.

Grails generating proper links when deployed behind proxy

Consider the following setup for a deployed Grails application.
the Grails application is deployed on a Tomcat server (tomcat7)
in front of Tomcat an Apache webserver is deployed
Apache does SSL offloading, and acts as a proxy for Tomcat
So far a quite standard setup, which I have used succesfully many times. My issue is now with the links generated by the Grails application, especially those for the redirects (the standard controller redirects, which occur all the time e.g. after succesfully posting a form).
One configuration is different from all the other applications so far: in this Grails application no serverURL is configured. The application is a multi-tenant application, where each tenant is given it's own subdomain. (So if the application in general is running under https://www.example.com, a tenant can use https://tenant.example.com.) Subdomains are set automagically, that is without any configuration at DNS or Apache level. Grails can do so perfectly, by leaving out the serverURL property in Config.groovy: it then resolves all url's by inspecting the client host.
However: when generating redirect-url's, Grails is not aware the website is running under https. All redirect url's start with http... I guess this is no surprise, because nowhere in the application it is configured we are using https: there is no serverURL config, and technically the application is running on the standard http port of Tomcat, because of the SSL offloading and proxying by Apache.
So, bottom line: what can I do to make Grails generate proper redirects?
I have tried to extend the DefaultLinkGenerator and override the makeServerURL() method. Like this:
class MyLinkGenerator extends DefaultLinkGenerator {
MyLinkGenerator(String serverBaseURL, String contextPath) {
super(serverBaseURL, contextPath)
}
MyLinkGenerator(String serverBaseURL) {
super(serverBaseURL)
}
def grailsApplication
/**
* #return serverURL adapted to deployed scheme
*/
String makeServerURL() {
// get configured protocol
def scheme = grailsApplication.config.grails.twt.baseProtocol ?: 'https://'
println "Application running under protocol $scheme"
// get url from super
String surl = super.makeServerURL()
println "> super.makeServerURL(): $surl"
if (surl) {
// if super url not matching scheme, change scheme
if (scheme=='https://' && surl?.startsWith('http://')) {
surl = scheme + surl?.substring(7)
println "> re-written: $surl"
}
}
return surl
}
}
(Maybe not the most beautiful code, but I hope it explains what I'd like to do. And I left out the bit about configuring this class in resources.groovy.)
When running this code strange things happen:
In the log you see the code being executed, and a changed url (http > https) being produced, but...
The redirect sent to the browser is the unchanged url (http)
And even worse: all the resources in the generated views are crippled: they now all start with // (so what should be a relative "/css/myapp.css" is now "//css/myapp.css")
Any help or insight would be appreciated!
Grails version is 2.1.1 by the way (running a bit behind on upgrades...).
It seems you're always talking https to the outside world, so your cleanest option is to solve the problem where it originates, at your Apache webserver. Add to httpd.conf Header edit Location ^http://(.*)$ https://$1, and you're done.
If you have limitations that force you to solve this in your application you could do the rewrite of the Location header field in a Grails after interceptor. Details of that solution are in this post.
Some years have past since this question was written, but problems remain the same or at least similar ;-)
Just in case anyone hits the same/similar issue (that Grails redirect-URLs are http instead of https) ... We had this issue with a Grails 3.3.9 application running on OpenShift. The application was running in HTTP mode (on Port 8080) and the OpenShift Loadbalancer was doing the SSL-Termination.
We solved it by putting
server:
use-forward-headers: true
into our application.yml. After that Grails works perfect and all the redirects created were correct with https://.
Hint: We have do not use grails.serverURL in our configuration

How can I get the current hostname and port when running Capybara with Poltergeist? [duplicate]

This question already has answers here:
Cucumber / Capybara -- how to get the host and port of the current execution
(3 answers)
Closed 4 years ago.
I have a test program that mimics a user's iOS device by sending chunks of data to a rails server. This program works fine outside of testing. Inside of testing, the program immediately times out since it cannot find the server.
My configuration, in env.rb:
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
Capybara.server_port = 3123
Capybara.app_host = "http://0.0.0.0:3123"
Inside the test, I've tried things like
puts Capybara.current_session.current_host
puts Capybara.current_session.current_url
and so forth, but those are all blank. Doing:
puts Capybara.current_session
produces a memory address, so I know that the current session is working.
This is with Capybara set up like so in the gemfile:
gem 'capybara', '~> 2.0.2
I've seen several questions based on this, but their answers just don't work for me. Specifically, Capybara does not appear to be setting the current host or url as I've specified, nor is it actually allowing me to see the contents of the current session. So how can I fix this and specify a particular port and url?
For capybara < 2.0:
Capybara.current_session.driver.rack_server.host
Capybara.current_session.driver.rack_server.port
Capybara 2.0:
Capybara.current_session.server.host
Capybara.current_session.server.port
I found the answer here: Cucumber / Capybara -- how to get the host and port of the current execution
Capybara.current_session.driver.server.port

Ruby on Rails: Active Admin and Heroku - dashboard error

I have some Ruby on Rails applications that are using Active Admin gem. Rails version is 3.2.6, Active Admin version is 0.4.4.
I am pushing these applications to Heroku, then doing migrations and everything is working fine. But after some time (when application restarts) i'm starting to get 404 error when trying to open admin page (like myapp/admin). In logs there is an error:
ActionController::RoutingError (uninitialized constant Admin::DashboardController)
Moreover, if i'm trying to open some other admin page (like myapp/admin/videos - to administer videos) everything is still working fine, but error 404 persists when opening Dashboard page.
I have tried to put
config.cache_classes = true
config.assets.compile = true
to my config files, but all the same.
Basically the scheme is as follows:
I make some changes to the app, commit the changes with "git add .", "git commit" and push it to Heroku
I open the /admin page on Heroku and it works fine
After application restarts i get 404 error when visiting /admin, but everything still works when accessing other admin pages, not dashboard
GoTo 1
I'm still unsure if the error appears when the app is restarted by itself (not by "heroku restart").
Any ideas why this is happening? Maybe someone can advice how to switch off this Dashboard and use my myapp/admin/videos as the default admin page?
I had this very same problem, and since it complaints about the ActiveAdmin Dashboard, and it is now deprecated I proceeded to update my dashboard.rb file to the new Dashboard style and that solved the problem.
(I got that file from here).
Hope it helps.
I got the same issue.
Check whether you have to upgrade ActiveAdmin to new version
When upgrading to a new version of ActiveAdmin you may need to run
rails generate active_admin:assets
If you get:
uninitialized constant Admin::DashboardController
Use the New default page for admin/dashboard.rb
which is like following,
ActiveAdmin.register_page "Dashboard" do
menu :priority => 1, :label => proc{ I18n.t("active_admin.dashboard") }
content :title => proc{ I18n.t("active_admin.dashboard") } do
div :class => "blank_slate_container", :id => "dashboard_default_message" do
span :class => "blank_slate" do
span I18n.t("active_admin.dashboard_welcome.welcome")
small I18n.t("active_admin.dashboard_welcome.call_to_action")
end
end
# Here is an example of a simple dashboard with columns and panels.
#
# columns do
# column do
# panel "Recent Posts" do
# ul do
# Post.recent(5).map do |post|
# li link_to(post.title, admin_post_path(post))
# end
# end
# end
# end
# column do
# panel "Info" do
# para "Welcome to ActiveAdmin."
# end
# end
# end
end # content
end

using webmock with cucumber

I am using webmock and it is not working for cucumber tests
In my Gemfile
gem 'vcr'
gem 'webmock'
And in my features/support.env.rb, I have
require 'webmock/cucumber'
WebMock.allow_net_connect!
When I run my cucumber tests I am getting this error.
Real HTTP connections are disabled. Unregistered request:
GET http://127.0.0.1:9887/__identify__ with headers
{'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}
Am I doing anything wrong or is sth missing?
First off, if you're using VCR, you don't need to configure webmock with the require 'webmock/cucumber' line and the WebMock.allow_net_connect! line. VCR takes care of any necessary WebMock configuration for you.
The request that is triggering the error looks like it's coming from Capybara. When you use one of the javascript drivers, capybara boots your app using a simple rack server, and then polls the special __identify__ path so it knows when it has finished booting.
VCR includes support for ignoring localhost requests so that it won't interfere with this. The relish docs have the full story but the short version is that you need to add VCR configuration like this:
VCR.config do |c|
c.ignore_localhost = true
end
I had the same error though do not use VCR. I was able to resolve this by adding:
require 'webmock/cucumber'
WebMock.disable_net_connect!(:allow_localhost => true)
to my env.rb file.
Expanding on Myron Marston's answer. If you need to keep localhost for something else, such as a Rack App, that you might want VCR to capture request for, you will need to create a custom matcher rather than ignore all localhost requests.
require 'vcr'
VCR.configure do |c|
c.hook_into :webmock
c.ignore_localhost = false
c.ignore_request do |request|
localhost_has_identify?(request)
end
end
private
def localhost_has_identify?(request)
if(request.uri =~ /127.0.0.1:\d{5}\/__identify__/)
true
else
false
end
end
If you use both RSpec and Cucumber, you might need to create two config files for WebMock (when using with VCR):
# spec/support/webmock.rb
# Config for RSpec
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
# features/support/webmock.rb
# Config for Cucumber
require 'webmock/cucumber'
WebMock.disable_net_connect!(allow_localhost: true)
Documenting this here for people to find when googling for __identify__. Errors look like...
Real HTTP connections are disabled.
Unregistered request: GET http://127.0.0.1:59005/__identify__