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

Already on GitHub? Sign in to your account

Flash message not detected by Capybara when root route is customized #7073

Closed
davidmles opened this Issue Jul 17, 2012 · 2 comments

Comments

Projects
None yet
2 participants

First of all, this issue is related to Capybara, and it's reported here: jnicklas/capybara#753

@jnicklas says this is not a problem of Capybara and does not want to spend time on this, so I think the next step is to scale 1 level and report the issue to the Rails devs:

The problem is when a custom root route is defined, together with the standard root route:

root to: "main#index"
get "(/page/:page)" => "main#index", as: "root"

My real application needs that customized root route to display a paginated list and customize the page param in the url.

In this case, when some controller generates a flash message and redirects to the root page, that flash message is NOT detected by Capybara in an integration test. Nonetheless, when using Selenium and a full Chrome browser to test it, then Capybara DOES detect it.

I have uploaded a simple app here that reproduces the issue: https://github.com/idavemm/flash_root

It contains 2 integration tests, one checks the flash message with the Selenium driver and the other without it. To run it:

ruby -Itest test/integration/flash_message_test.rb

Basically, what's needed to replicate the issue is the following:

  • 3 routes: the normal root, a custom root, and one for the controller that generates the flash and redirects to the root. The custom root is the key to replicate the issue. In this case it has no use, but in my real application it is used to show a paginated list.
FlashRoot::Application.routes.draw do
  root to: "main#index"

  get "(/page/:page)" => "main#index", as: "root"

  get "/set_flash" => "flash#info"
end
  • One main controller. It is void, but its view only has a link to generate a flash message:
<h1>Main page</h1>

<%= link_to "Set flash", set_flash_path %>
  • One controller that simply generates the flash message and redirects to root:
class FlashController < ApplicationController
  def info
    flash[:info] = "This is a flash message"
    redirect_to root_path
  end
end

That's it. Now for the integration tests:

require 'test_helper'

class FlashMessageTest < ActionDispatch::IntegrationTest
  test "should show a success flash message with JS" do
    Capybara.current_driver = Capybara.javascript_driver

    visit root_path
    click_link "Set flash"

    assert page.has_content? "This is a flash message"
  end

  test "should show a success flash message without JS" do
    visit root_path
    click_link "Set flash"

    assert page.has_content? "This is a flash message"
  end
end

The test_helper.rb is configured as normal: https://github.com/idavemm/flash_root/blob/master/test/test_helper.rb

Owner

pixeltrix commented Jul 17, 2012

It looks as though Rack::Test is sensitive to whether the root url has a trailing slash or not. Your second root_path is the one that gets used and gives an empty string rather than '/' because of a bug in ActionDispatch::Routing::Mapper.normalize_path (it should put the leading slash outside the parentheses). I'll fix normalize_path at which point you should change it to a single route get '/(page/:page)' => 'main#index', :as => :root. In the meantime you can fix it by creating your own root_path helper, e.g:

FlashRoot::Application.routes.draw do
  get "/" => "main#index", :as => :default_root
  get "/page/:page" => "main#index", :as => :paged_root
  get "/set_flash" => "flash#info"
end

FlashRoot::Application.routes.named_routes.module.module_eval do
  def root_path(*args)
    options = args.extract_options!

    if args.any? || options.key?(:page)
      paged_root_path(*args, options)
    else
      default_root_path(options)
    end
  end

  def root_url(*args)
    options = args.extract_options!
    root_path(*args, options.merge(:only_path => false))
  end
end

With this routes.rb file the integration tests pass. Note that this problem doesn't occur with standard Rails integration tests because it's process method performs some url mangling that ends up adding the slash.

Thanks a lot @pixeltrix, your workaround works perfectly.

I would be glad to know when the fix is done :)

@pixeltrix pixeltrix closed this in d8745de Jul 17, 2012

@pixeltrix pixeltrix added a commit that referenced this issue Jul 17, 2012

@pixeltrix pixeltrix Add support for optional root segments containing slashes
Optional segments with a root scope need to have the leading slash
outside of the parentheses, otherwise the generated url will be empty.
However if the route has non-optional elements then the leading slash
needs to remain inside the parentheses otherwise the generated url
will have two leading slashes, e.g:

Blog::Application.routes.draw do
  get '/(:category)', :to => 'posts#index', :as => :root
  get '/(:category)/author/:name', :to => 'posts#author', :as => :author
end

$ rake routes
  root GET /(:category)(.:format)              posts#index
author GET (/:category)/author/:name(.:format) posts#author

This change adds support for optional segments that contain a slash,
allowing support for urls like /page/2 for the root path, e.g:

Blog::Application.routes.draw do
  get '/(page/:page)', :to => 'posts#index', :as => :root
end

$ rake routes
root GET /(page/:page)(.:format) posts#index

Fixes #7073
(cherry picked from commit d8745de)
71d274d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment