Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using url_for in Rails/Capybara/Poltergeist spec sends the driver to example.com instead of the app

If I call url_for within a feature spec, it returns an absolute URL starting with http://www.example.com/. Capybara will happily attempt to load pages on that site, but that has nothing to do with my app. Here are minimal steps to reproduce the issue:

Start with this Gemfile:

source 'https://rubygems.org'

gem "sqlite3"
gem "jquery-rails"
gem "draper"
gem "rails", '4.1.0'
gem "therubyracer"
gem "uglifier"
gem "rspec-rails"
gem "capybara"
gem "poltergeist"
gem "launchy"

Run the following:

bundle
rails new myapp -O
cd myapp
rm Gemfile Gemfile.lock
rails generate controller Test test
rails generate rspec:install
mkdir spec/features

Comment out the lines in spec/spec_helper.rb that say they should be removed when not using ActiveRecord, and then create spec/features/feature_spec.rb with the following contents:

require 'capybara/poltergeist'

Capybara.configure do |config|
  config.javascript_driver = :poltergeist
end

require 'spec_helper'

describe "nothing", js: true do
  specify do
    visit(url_for(controller: :test, action: :test))
    save_and_open_page
  end
end

Finally, run rake spec and you will see the example.com page pop up in a browser. I have verified this behavior back to Rails 3.2.17.

Why is this happening, and is there a way to get URLs for the app being tested instead of example.com?

Edit: some things I have found looking into this more:

ActionDispatch::Routing::UrlFor.url_for is called from RSpec examples. It has only_path defaulting to false.

ActionView::RoutingUrlFor is the version you get in, say, a view. It has only_path defaulting to true, which works much better.

This commit to the rspec-rails gem probably caused the problem, by adding www.example.com as the default host. There is no explanation anywhere about why this host is an appropriate/useful choice.

like image 486
pdg137 Avatar asked Apr 18 '14 23:04

pdg137


2 Answers

The problem manifests for the following reasons:

  1. You are using Poltergeist, which uses PhantomJS, which is perfectly capable of opening any URL.
  2. You are using the url_for helper which needs to know the domain it should be generating a url for. When used inside a Rails view or controller, Rails supplies the domain based on what was used to make the request. When outside of a view or a controller, like in an ActionMailer or Capybara test, the domain is unknown. Capybara defaults the unknown domain to example.com.

So everything is working the way it should. Now, it happens to not be the way that you want it to work. However, if you want it to work how you would like you should do one of the following things:

  1. Use the path_only option in url_for to tell it not to use the host part.
  2. Use the host option in url_for to specify the correct host.
like image 154
Alex Peachey Avatar answered Oct 05 '22 03:10

Alex Peachey


This is how those gems work. The http://example.com is irrelevant to your app. In general, you should not have fully hard-coded paths in your app. Rails attempts to determine your local domain (for specs this is example.com, which is configurable) and creates paths off of that.

The idea here is that you have a base URL which may change. Say, for staging I use a Heroku local app: randomname-123-staging.heroku.com. My urls will be prefixed with that. However, in production I own a domain name. My urls there will start with mydomain.com. It makes no sense for me to have to update all of my URLs based on the environments base domain; this should be (and is) provided by Rails.

By using a generic domain, which is supposed to be guaranteed to not resolve to a real IP, the specs help you code to this possibility.

like image 45
Aaron K Avatar answered Oct 05 '22 04:10

Aaron K