New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default_url_options is being ignored #1275

Closed
beydogan opened this Issue Jan 13, 2015 · 33 comments

Comments

Projects
None yet
@beydogan

beydogan commented Jan 13, 2015

In my rspec_helper.rb I defined;

Rails.application.routes.default_url_options[:host] = 'lvh.me'

It works fine for non-feature specs but doesn't work for feature specs. Host for urls are equal to "www.example.com" in feature specs.

I've debugged and in feature_example_group.rb file, default_url_options is always an empty hash.

https://github.com/rspec/rspec-rails/blob/master/lib/rspec/rails/example/feature_example_group.rb#L18

found the following solution for this issue;
beydogan@b590c82

Is this valid?

@cupakromer

This comment has been minimized.

Member

cupakromer commented Jan 13, 2015

What problem is this solving for you? I'd like to understand where this is causing an issue.

@beydogan

This comment has been minimized.

beydogan commented Jan 13, 2015

@cupakromer The problem is; when I set default_url_options in my spec/rails_helper.rb or environments/test.rb. It's being ignored in feature specs.

I set like following;

Rails.application.routes.default_url_options[:host] = 'lvh.me'

But in a feature spec;
posts_url is equal to http://www.example.com/posts instead of http://lvh.me/posts

@cupakromer

This comment has been minimized.

Member

cupakromer commented Jan 13, 2015

Ok, I dug into this a bit. It's a little more complicated that it seems. Here's what is going on.

The Rails test cases, the RSpec example groups are a thin shim on top of these, setup an attribute default_url_options. If this is not set or defined, then the call chain falls back on whatever the current routes scope has configured. By default it has nothing configured for :host and the value is nil.

If we removed or comment out the line:

default_url_options[:host] ||= ::RSpec::Rails::FeatureExampleGroup::DEFAULT_HOST

You would get the following error:

Missing host to link to! Please provide the :host parameter, set
default_url_options[:host], or set :only_path to true

The reason your proposed solution in #1276 appears to work is simply because the example's default_url_options is nil and so it falls back to the routes which you have customized in the spec/rails_helper.rb. For other spec types, which don't have a :host option set by default, the route generation falls back to the session set by Rails' test case. This depending on the spec type and is normally set to a default of www.example.com or pulled from a request, but is all handled by Rails' test cases. This is not set for feature specs, because the session is handled by Capybara which does not set something for it.

So unfortunately, your solution is basically a no-op and is the same as deleting that if block. For the majority of cases, people don't customize the host option and they would get the above exception.

We could change it to:

if respond_to?(:default_url_options)
  default_url_options[:host] ||= (
    app.routes.default_url_options[:host] ||
    ::RSpec::Rails::FeatureExampleGroup::DEFAULT_HOST
  )
end

However, this will cause other problems:

require 'rails_helper'

Rails.application.routes.default_url_options[:host] = 'lvh.me'
RSpec.feature "Relative path hosts set by Capybara", type: :feature do
  it "causes a mismatch between the URLs" do
    visit widgets_path
    expect(current_url).to eq widgets_url
  end
end

Results in a failure:

Failures:

  1) Relative path hosts set by Capybara causes a mismatch between the URLs
     Failure/Error: expect(current_url).to eq widgets_url

       expected: "http://lvh.me/widgets"
            got: "http://www.example.com/widgets"

       (compared using ==)
     # ./spec/features/capybara_mismatch_spec.rb:7:in `block (2 levels) in <top (required)>'

This is because Capybara uses Capybara.default_host to generate the "full" URL under the hood. The default host just happens to be "www.example.com". However, it won't actually use that web server. It just uses the value to generate the full URL while still using it's local server. You have to configure Capybara to use an external app host.

There's another wrinkle. Capybara expects the "http://" part in the URL, if it's not present the URL is created as: "http:///widgets". I'm not sure this is a bug, you'd have to ask them. So while Rails is happy to have "lvh.me", Capybara would need "http://lvh.me".

We are tightly coupled to Capybara right now. As you can see from the feature example group file it's essentially just all code necessary to handle Capybara's design decisions. It also causes a lot of confusion for newer users who do not understand this tight dependency.

I really do not want to add yet another special case for Capybara. Let me think on this a bit. I have a potential solution in mind but will need to think on it for a day.

I hope this helps understand what's happening.

@simonoff

This comment has been minimized.

simonoff commented Sep 29, 2015

Any fix?

@JonRowe

This comment has been minimized.

Member

JonRowe commented Sep 30, 2015

Not yet, see @cupakromer's comments above, you can try setting the Capybara default host as a work around?

@simonoff

This comment has been minimized.

simonoff commented Sep 30, 2015

@JonRowe I'm not using Capybara. Same thing in controller tests.

@JonRowe

This comment has been minimized.

Member

JonRowe commented Sep 30, 2015

This issue only affects feature tests, which use Capybara.

@simonoff

This comment has been minimized.

simonoff commented Sep 30, 2015

Again. This issue in controllers tests too.
Only way to fix it is:

[ApplicationController, ActionController::Base].each do |klass|
  klass.class_eval do
    def default_url_options(options = {})
      { :host => "example.com" }.merge(options)
    end
  end
end
@JonRowe

This comment has been minimized.

Member

JonRowe commented Sep 30, 2015

Can you elaborate what symptoms you are seeing; It's possible you are suffering from the symptoms of the rails behaviour (which we cannot fix) or a different bug altogether.

@simonoff

This comment has been minimized.

simonoff commented Sep 30, 2015

I'm testing subdomains and instead of _path helpers using _url in my controller. It's only what I set.

@JonRowe

This comment has been minimized.

Member

JonRowe commented Sep 30, 2015

Sorry you're going to have to elaborate further, when reporting issues it's best to actually provide an example showing what you're trying to do, and the unexpected outcome of that attempt. Even better if it's actually executable (but please... don't point me to a rails app repo, I don't have time to comb through an entire app).

@emaiax

This comment has been minimized.

emaiax commented Nov 10, 2015

I'm still having lots of problems when testing myroute_url using different subdomains.

Any thoughts?

@rvsingh

This comment has been minimized.

rvsingh commented Mar 29, 2016

Facing the same issue in features spec with Rspec and Capybara.

Tried adding the config in application.rb
Rails.application.default_url_options[:host] = Figaro.env.domain_name

Further tried overriding Capybara config

Capybara.configure do |config| config.default_host = Figaro.env.domain_name end

But 'about_us_url' returns "http://www.example.com/about_us" instead of "http://localhost:3000/about_us". This causes email specs to fail where we check if the url has been added to an email.

@JonRowe

This comment has been minimized.

Member

JonRowe commented Mar 29, 2016

For emails you also have to configure ActionMailer::Base.default_url_options

@rvsingh

This comment has been minimized.

rvsingh commented Mar 29, 2016

That is configured. This issue is not related to email. While running any spec if I debug and check what urls are generated with 'xyz_url' method, it always prepends it with with 'http://www.example.com/'. It should ideally prepend whatever value is set to Rails.application.default_url_options[:host] in test environment.

@JonRowe

This comment has been minimized.

Member

JonRowe commented Mar 29, 2016

RSpec doesn't have any value for this url, it comes from Capybara or from your config.

@rvsingh

This comment has been minimized.

rvsingh commented Mar 29, 2016

While debugging the spec if I check value of
self
it gives

#RSpec::ExampleGroups::SignUp:0x007fe910f18168

And when we check value of

self.url_options

it gives

result = Hash (1 element)
host => www.example.com

So as you said, I think, the value is being set by either capybara or may be Rspec. Any thoughts?

@JonRowe

This comment has been minimized.

Member

JonRowe commented Mar 29, 2016

That'll be the value from the Rails helpers we mix in, you need to check
your Rails configuration.

@rvsingh

This comment has been minimized.

rvsingh commented Mar 29, 2016

Hey @JonRowe

I checked and found that issue is exactly where @beydogan is suggesting in the first comment.
In the line 19 of feature_example_group.rb

default_url_options[:host]

is always nil, no matter where I set the default_url_options.
And thus in the same line default host is set to www.example.com

As of now in my application I have set the default host at all possible places.

config/environments/test.rb

  Rails.application.default_url_options = {:host => Figaro.env.domain_name }
  Rails.application.routes.default_url_options = {:host => Figaro.env.domain_name }

spec/support/capybara.rb

Capybara.app_host = 'http://localhost'
Capybara.always_include_port = true

Capybara.configure do |config|
  config.default_host = Figaro.env.domain_name
end
@jufemaiz

This comment has been minimized.

jufemaiz commented May 18, 2016

Any further comments?

@samphippen

This comment has been minimized.

Member

samphippen commented Jul 5, 2016

Sample app here: https://github.com/samphippen/demo_app_for_1275.git

Worth noting: this explicitly is not supposed to work in controller specs according to someone from rails core.

So request specs work, and feature specs don't work here.

@kurko

This comment has been minimized.

kurko commented Sep 26, 2016

For future reference: I tried all sorts of variations (e.g config.action_mailer.default_url_options = { host: 'www.example.com' }, Capybara's host etc), nothing worked. I then added Rails.application.routes.default_url_options[:host] = 'www.example.com' after the end (end of file, outside the config block) in config/environments/tests.rb and it worked.

@caironoleto

This comment has been minimized.

caironoleto commented Nov 11, 2016

I tried everything described in this issue too and my tests still broken.

I'm using apartment with custom domains and it's necessary setup default_url_options with custom domain.

@benbonnet

This comment has been minimized.

benbonnet commented Feb 16, 2017

anything found and tried, always end up with an error. prefixing for now with kind of the following visit("http://custom_domain/itll_work_someday"); but that quite feels weird. Hope there'd be a clear explanation

@sharplet

This comment has been minimized.

sharplet commented Mar 22, 2017

FWIW, I just ran into this issue, and found that if I call the url helper before making any requests in my request spec, I get the expected hostname:

+expected_url = foo_bars_url(foo)
 
 get url_for(foo)
 
 expect(json).to include(
   "links" => a_hash_including(
-    "bars" => foo_bars_url(foo)
+    "bars" => expected_url
   )
 )

UPDATE: I'm no longer seeing this behaviour. It could have been an error on my part.

@wolfpakz

This comment has been minimized.

wolfpakz commented Apr 6, 2017

I'm seeing the exact same situation as @rvsingh. Any attempt to configure default_url_options is effectively ignored.

I have been able to work around the issue by acquiescing, and re-configuring default_url_options to use the host "www.example.com". Thanks to @kurko for the suggestion.

This is incredibly frustrating because rspec-rails is effectively forcing you to use "www.example.com". If you have any other desire, you're left with options that feel wrong.

@JonRowe

This comment has been minimized.

Member

JonRowe commented Apr 10, 2017

I'm sorry you're frustrated, but we just set a default value if one isn't present, it's up to Rails how it uses that value, I suspect its being cached in a way making it difficult to override.

@aleon

This comment has been minimized.

aleon commented Apr 26, 2017

For controller tests inside the Rails.application.configure block seemed to work for me:

  config.action_controller.default_url_options = {
    host: ENV['HOST']
  }
@RasPat1

This comment has been minimized.

RasPat1 commented Oct 17, 2017

Same issue. Alternate resolution.

I noticed that a random feature test was failing inconsistently in both dev machine and CI environments. After reproducing with rspec --seed and getting consistent failures tried many of the approaches here. Unfortunately, none of them quite worked for me. The route helpers would still resolve incorrectly. i.e. my_page_url = www.example.com/my_page.

The interesting thing was that only the first feature test would fail. For all the subsequent feature tests in any feature testing file, the URL helpers would still resolve incorrectly, but the visit would be successful. In other words, visiting www.example.com/my_page would render my page correctly and run the necessary assertions as intended.

Simplest hack possible for me was just visiting one page before running the real tests:

  # HACK: https://github.com/rspec/rspec-rails/issues/1275
  config.before(:all, type: :feature) do
    visit(my_page_path)
  end

Added this to the RSpec configure block in my rails helper and haven't had any problems since.

ruby 2.3.4p301
rails 5.1.1
rspec 3.6.0
capybara 2.14.0

@Xosmond

This comment has been minimized.

Xosmond commented Nov 21, 2017

These options are ignored on system tests.
Had to do this to solve the problem:

  def visit_to(path)
    visit("http://test.lvh.me"+":#{Capybara.server_port}"+path)
  end
@lulalala

This comment has been minimized.

lulalala commented Feb 14, 2018

I think this is just a Rails issue. There are many places to set default_url_options, but they either work on console but not web, or the other way around:

In Rails 5.1.4, I have tested the following scenarios on web and console (but not in Rspec):

    # in development.rb
    config.action_controller.default_url_options({:protocol => 'https'})
    config.action_controller.default_url_options(:protocol => 'https')
    # Does not work

    # in development.rb, outside config block
    Rails.application.routes.default_url_options[:protocol] = 'https'
    # Does not work, but works under console

    # in routes.rb
    Rails.application.routes.draw do
      default_url_options protocol: :https
    # Does not work, but works under console

    # in ApplicationController
    def default_url_options
      { protocol: :https }
    end
    # Works in browser, but does not work under console

    # in development.rb
    config.action_controller.default_url_options= {:protocol => 'https'}
    # Works in browser, but does not work under console
@uestcsp

This comment has been minimized.

uestcsp commented Oct 17, 2018

any one fix this problem?
my case is : Rails 5.2.1, ruby 2.5.1
in my environment it works well

@benoittgt

This comment has been minimized.

Member

benoittgt commented Oct 19, 2018

Regardless of the last comments. I think we can close this issue. Feel free to comment if we think we should still look at it.

@benoittgt benoittgt closed this Oct 19, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment