Reputation: 690
I've got an optional locale parameter in my route.
# config/routes.rb
scope '(:locale)' do
resources :orders
resources :line_items
resources :carts
root 'store#index', as: 'store_index', via: :all
end
# config/initializers/i18n.rb
#encoding: utf-8
I18n.default_locale = :en
LANGUAGES = [
['English', 'en'],
["Español".html_safe, 'es']
]
If I want to test my view like so,
# /spec/views/carts/edit.html.erb_spec.rb
require 'rails_helper'
RSpec.describe "carts/edit", type: :view do
let(:cart) { create(:cart) }
before(:each) do
assign(:cart, cart)
end
it "renders the edit cart form" do
render
assert_select "form[action=?][method=?]", cart_path(cart), "post" do
end
end
end
I get an error
/Users/burak/.rvm/rubies/ruby-2.7.3/bin/ruby -I/Users/burak/.rvm/gems/ruby-2.7.3/gems/rspec-core-3.10.1/lib:/Users/burak/.rvm/gems/ruby-2.7.3/gems/rspec-support-3.10.2/lib /Users/burak/.rvm/gems/ruby-2.7.3/gems/rspec-core-3.10.1/exe/rspec spec/views/carts/edit.html.erb_spec.rb
F
Failures:
1) carts/edit renders the edit cart form
Failure/Error: <%= form_with(model: cart) do |form| %>
ActionView::Template::Error:
No route matches {:action=>"show", :controller=>"carts", :format=>nil, :locale=>#<Cart id: 1252, created_at: "2021-04-27 13:45:09.842106000 +0000", updated_at: "2021-04-27 13:45:09.842106000 +0000">}, missing required keys: [:id]
Did you mean? cart_url
carts_url
carts_path
new_cart_url
# ./app/views/carts/_form.html.erb:1:in `_app_views_carts__form_html_erb__2203507052991398875_16560'
# ./app/views/carts/edit.html.erb:3:in `_app_views_carts_edit_html_erb___4312857554407673387_16540'
# ./spec/views/carts/edit.html.erb_spec.rb:11:in `block (2 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# ActionController::UrlGenerationError:
# No route matches {:action=>"show", :controller=>"carts", :format=>nil, :locale=>#<Cart id: 1252, created_at: "2021-04-27 13:45:09.842106000 +0000", updated_at: "2021-04-27 13:45:09.842106000 +0000">}, missing required keys: [:id]
# Did you mean? cart_url
# carts_url
# carts_path
# new_cart_url
# ./app/views/carts/_form.html.erb:1:in `_app_views_carts__form_html_erb__2203507052991398875_16560'
Finished in 0.02277 seconds (files took 0.60597 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/views/carts/edit.html.erb_spec.rb:10 # carts/edit renders the edit cart form
So as far as I understood, that's because my cart object is being passed with a :locale
keyword to somewhere. If I explicitly pass the URL to form_with
in my _form
partial, then the error disappears but then that breaks some other tests. How can I fix this?
Here are the necessary partials and views.
# app/views/carts/edit.html.erb
<h1>Editing Cart</h1>
<%= render 'form', cart: @cart %>
<%= link_to 'Show', @cart %> |
<%= link_to 'Back', carts_path %>
# app/views/carts/_form.html.erb
<%= form_with(model: cart) do |form| %>
<% if cart.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(cart.errors.count, "error") %> prohibited this cart from being saved:</h2>
<ul>
<% cart.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Upvotes: 0
Views: 945
Reputation: 10422
So the route for the URL that your form wants to point to is (in the format you'll see from rails routes
):
(/:locale)/carts/:id(.:format)
What looks to be happening is that when your view spec is rendering, it calls cart_path(cart)
to build the form's action
attribute. And what's happening is that helper method, cart_path
, tries to populate the parameterised parts of the path from left to right, and it uses cart
to try and populate the :locale
segment instead of the :id
segment.
You don't see this happen in the context of being rendered by a controller action in a rails server
, because the controller has a concept of locale
having a default value, and so it understands that cart
represents the second, :id
part of the path.
If you want to ensure that your views can behave similarly when tested in isolation, then you can add a default value to your routes file:
scope '(:locale)', defaults: { locale: 'en' } do
...
And in your config/environments/test.rb
, add a further default:
Rails.application.configure do
# ...
routes.default_url_options[:locale] = 'en'
end
Setting the default in routes.rb
will allow your view file to correctly generate a valid path; adding a default_url_options
means you can write cart_path(cart)
in your view spec and it will know to use 'en' as the locale by default. Without it, you'd have to assert that the action path was cart_path(cart, locale: 'en')
.
Upvotes: 2