Skip to content
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 · 36 comments
Closed

Default_url_options is being ignored #1275

beydogan opened this issue Jan 13, 2015 · 36 comments

Comments

@beydogan
Copy link

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
Copy link
Member

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

@beydogan
Copy link
Author

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
Copy link
Member

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
Copy link

Any fix?

@JonRowe
Copy link
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
Copy link

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

@JonRowe
Copy link
Member

JonRowe commented Sep 30, 2015

This issue only affects feature tests, which use Capybara.

@simonoff
Copy link

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
Copy link
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
Copy link

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

@JonRowe
Copy link
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
Copy link

emaiax commented Nov 10, 2015

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

Any thoughts?

@rvsingh
Copy link

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
Copy link
Member

JonRowe commented Mar 29, 2016

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

@rvsingh
Copy link

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
Copy link
Member

JonRowe commented Mar 29, 2016

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

@rvsingh
Copy link

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
Copy link
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
Copy link

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
Copy link

Any further comments?

@fables-tales
Copy link
Member

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link
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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link
Member

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.

tahb added a commit to DFE-Digital/teaching-vacancies that referenced this issue Feb 19, 2019
setting domains and protocols site wide was harder to tweak as first thought since Capybara’s feature tests do not respect the universal protocol setting of HTTPS, which our views and use of _url start to create. This led me back to this issue which reminds us why we have the domain configured at the bottom and that we might need to do soemthing similar for protocol.. rspec/rspec-rails#1275
tahb added a commit to DFE-Digital/teaching-vacancies that referenced this issue Feb 19, 2019
setting domains and protocols site wide was harder to tweak as first thought since Capybara’s feature tests do not respect the universal protocol setting of HTTPS, which our views and use of _url start to create. This led me back to this issue which reminds us why we have the domain configured at the bottom and that we might need to do soemthing similar for protocol.. rspec/rspec-rails#1275
@abo-elleef
Copy link

Any fixes for this problem ?

@grishkovelli
Copy link

I faced with the same issue. I found out that RAILS_ENV == development, so RAILS_ENV=test rspec helped me.

@eliotsykes
Copy link
Contributor

eliotsykes commented Mar 30, 2022

If you set config.action_controller.default_url_options in config/environments/test.rb to the correct value, you can reuse it in feature specs by setting self.default_url_options = ... in a before block as below:

# File spec/rails_helper.rb
RSpec.configure do |config|
  config.before(type: :feature) do
    self.default_url_options = ApplicationController.default_url_options
  end
end

This fixes the issue originally described.

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

Successfully merging a pull request may close this issue.