Testing an AJAX dropdown - ruby-on-rails

I have two AJAX dropdowns, the first shows CountryObjs, and the second show the CityObjs belonging to that CountryObj. Selecting a different CountryObjs sends an AJAX request that updates the CityObjs for the new CountryObj. I'd like a simple test that passes a CountryObj and checks the CityObjs that are returned.
routes.rb
match '/update_cities', to: 'guides#update_cities', via: 'get'
Guide belongs_to CityObj and CountryObj, and they in turn has_many Guides.
guides_controller.rb
def update_cities
#cities = CityObj.where("country_obj_id = ?",
params[:country_obj_id]).order(:name)
debugger
respond_to do |format|
format.js
end
I'm still confused by testing in Rails. I'm not using rspec. Looking at my other tests that get :index and get :show, it seems like I should be able to do something like the following to pass a CountryObj and get into the update_cities method:
test "ajax dropdown" do
get :update_cities, city_obj: CityObj.last
end
But that gives an error
ActionController::UnknownFormat Exception: ActionController::UnknownFormat

To make an XmlHttpRequest (AJAX) request using GET you should try like this
test "ajax dropdown" do
xhr :get, :update_cities, city_obj: CityObj.last
end

Related

Render to named path

I have a rails project with the following route:
get 'login', to: 'user_sessions#new', as: :login
In my UserSessionsController I have
def create
#user_session = UserSession.new(params[:user_session])
respond_to do |format|
if #user_session.save
# Do all the happy stuff
else
format.html { render :action => 'new' }
format.xml { render xml: #user_session.errors, status: :unprocessable_entity }
end
end
end
That's working ok, except that when the user enters incorrect parameters the route is via /user_sessions instead of /login, which is untidy (and means my test assertions are confusing).
Obviously I could just redirect_to login_path, but then my #user_session.errors don't seem to be available so by page doesn't show what was wrong.
How do I redirect back to /login and still have the errors show?
Edit:
It looks as if Rails makes this difficult because it's something I shouldn't try to do. The RESTful path isn't really something the user cares about so I shouldn't be using it as part of my UI testing. Instead, I am looking at the actual content of the rendered page, which the user does care about. Thanks all.
You can add
post 'login', to: 'user_sessions#create', as: :post_login
and change the form action accordingly.
This is happening because when you get validation errors in your form then you are on create action and not new action. Your create action simply render your new actions template with errors, it doesn't send a request to server and hence your url remains same so to fix it you can simply change the route for your create action to this:
post 'login', to: 'user_sessions#create', as: :login
Update:
You'll just have to change your route for create action and then make changes in your form, something like this:
<%= from_for #resource, url: login_path do |f| %>
// form fields
<% end %>
If you'll inspect your form you'll see that its method is POST so when you'll submit it, your form will send a POST request and when you hit /login in your browsers address bar it'll send a GET request so in first case you'll go to create action and in second one you'll go to new action

where does the url routes get resolved when you call render in rails controller actions

Is there any method that i should look at in rails3.2 source code so as to know where the navigation or the url part of the render call get resolved?
The reason is, i have a small app in which url is of the form
www.example.com/bob/edit
the above route as it suggests renders the edit form.EDIT: i was able to get to this route by modifying response on the link_to helper.
def update
#when validation passes
redirect_to #user
#when validation fails
respond_to do |format|
format.html {render :action => "edit"}
end
end
Now the problem is when a validation error occurs on submission to update action of users_controller,
the url becomes
www.example.com/users/bob/edit
config/routes.rb
get "users/new", to: => "users#new"
resources :users
as you can see there's nothing interesting happening in routes,
in models/user.rb
def to_param
"#{name}"
end
in views/edit.html.erb
form_for(#user) do |f|
end
Observation: here when the form is rendered afresh, form 'action' points to "users/bob" but when the form is re-rendered 'cos of validation error, form action mysteriosly changes to "users/" which is weired and if i remove the to_param in user.rb model it works fine
Though its not such a big deal, i was thinking where, if i needed to override the url that is generated on render call, to change?????
Any suggestions and pointers to explore are wecome....
I'm not sure how you're getting the URLs you're getting, but a general answer to your question would be it doesn't. The URL you see after sending a request is the URL the request was sent to (or redirected to), not that of the page you came from, nor that of the template you render in the end. In your case, I'm guessing the problem is that you created a custom URL for the edit page, but not for update, and your form_for(#user) is sending the request to your update URL (probably PUT "/users/bob").
To fix this, the first thing is to create your custom update route. Maybe something like:
put ":id/update", to: => "users#update"
And then have your form_for use that URL:
form_for(#user, :url => "#{#user.to_param}/update")

Rails 3, Custom Actions, and HTML request methods

I don't really understand the pro's and con's of using "post" vs "get" vs "put" requests, on custom controller actions, and whether to use links or forms/buttons.
So let's say I have a simple to-do list with tasks, and a tasks controller, and I want a "complete" action where I find a specific task in the db and update it's status attribute from "incomplete" to "complete."
def complete
#task = Task.find(params[:id])
if #task.update_attributes(:status => "complete")
redirect_to tasks_url, :notice => "Completed!"
else
redirect_to tasks_url, :error => "Whoops."
end
end
What's the best practice way to define this route, which HTML request method should I use (post? put? get?), and should I use a plain link or a form? (and note: assume my user security model is all figured out with devise, and appropriate before filters, etc.)
And most of all, how would I articulate all this in a Rails 3 routes.rb file?
Note, the below code wasn't really working for me:
#routes.rb
resources :tasks do
members do
post 'complete'
end
end
so currently I'm using this instead:
#routes.rb
match 'tasks/:id/complete', 'tasks#complete', :as => "complete_task"
#view
= link_to "Complete", complete_task_path(:id => #task.id)
But this triggers a get request, and I feel like it should be a "put" or a "post." Or should it be a link at all? Should it be a form with hidden fields?
"link_to" method usually generates an anchor tag ie "<a></a>", ie a regular GET request
to do a POST request using link_to you should do the following
= link_to "Complete", complete_task_path(:id => #task.id), :method => :post
Remember if javascript is disabled in the browser, the above statement will fall back to a GET request instead of POST.

Simulating an XHR GET request

In my RSpec tests, I need to simulate an AJAX GET request to the index action, and have been using the code as described in both the Rails docs and the RSpec book:
xhr :get, :index
This always fails though, as the test tries to load the show action (without any parameters) rather than the specified index action.
The controller action is:
def index
#contacts = Contact.all
respond_to do |format|
format.html
format.js {
render :update do |page|
page.replace_html :contact_search_results, :partial => 'contacts'
end
}
end
end
The error thrown by running the spec is (showing the :show action being used):
ActionView::TemplateError in 'ContactsController as an administrator user when
showing the index of contacts' as an AJAX request should render results into the
contact_search_results element'
contact_url failed to generate from {:action=>"show", :controller=>"contacts",
:id=>#<Contact id: nil, first_name: nil, ....>}
Does anyone know how I can simulate an AJAX call the index action in tests?
Thanks!
Actually I think you're misunderstanding the error. Somewhere along the way Rails is trying to call contact_url and the parameters are wrong. My suspicion is that it is indeed calling the index action which then renders the contact partial. If I'm right, the contacts partial is the location of the issue. I would recommend reviewing the contacts partial for any possible errors. If you're still having trouble, please post the body of your contacts partial.
You're trying to make a URL to a non-persisted Contact object. You can see that in the message: :id=>#<Contact id: nil

Rails redirect_to post method?

redirect_to :controller=>'groups',:action=>'invite'
but I got error because redirect_to send GET method I want to change this method to 'POST' there is no :method option in redirect_to what will I do ? Can I do this without redirect_to.
Edit:
I have this in groups/invite.html.erb
<%= link_to "Send invite", group_members_path(:group_member=>{:user_id=>friendship.friend.id, :group_id=>#group.id,:sender_id=>current_user.id,:status=>"requested"}), :method => :post %>
This link call create action in group_members controller,and after create action performed I want to show groups/invite.html.erb with group_id(I mean after click 'send invite' group_members will be created and then the current page will be shown) like this:
redirect_to :controller=>'groups',:action=>'invite',:group_id=>#group_member.group_id
After redirect_to request this with GET method, it calls show action in group and take invite as id and give this error
Couldn't find Group with ID=invite
My invite action in group
def invite
#friendships = current_user.friendships.find(:all,:conditions=>"status='accepted'")
#requested_friendships=current_user.requested_friendships.find(:all,:conditions=>"status='accepted'")
#group=Group.find(params[:group_id])
end
The solution is I have to redirect this with POST method but I couldn't find a way.
Ugly solution: I solved this problem which I don't prefer. I still wait if you have solution in fair way.
My solution is add route for invite to get rid of 'Couldn't find Group with ID=invite' error.
in routes.rb
map.connect "/invite",:controller=>'groups',:action=>'invite'
in create action
redirect_to "/invite?group_id=#{#group_member.group_id}"
I call this solution in may language 'amele yontemi' in english 'manual worker method' (I think).
The answer is that you cannot do a POST using a redirect_to.
This is because what redirect_to does is just send an HTTP 30x redirect header to the browser which in turn GETs the destination URL, and browsers do only GETs on redirects
It sounds like you are getting tripped up by how Rails routing works. This code:
redirect_to :controller=>'groups',:action=>'invite',:group_id=>#group_member.group_id
creates a URL that looks something like /groups/invite?group_id=1.
Without the mapping in your routes.rb, the Rails router maps this to the show action, not invite. The invite part of the URL is mapped to params[:id] and when it tries to find that record in the database, it fails and you get the message you found.
If you are using RESTful routes, you already have a map.resources line that looks like this:
map.resources :groups
You need to add a custom action for invite:
map.resources :groups, :member => { :invite => :get }
Then change your reference to params[:group_id] in the #invite method to use just params[:id].
I found a semi-workaround that I needed to make this happen in Rails 3. I made a route that would call the method in that controller that requires a post call. A line in "route.rb", such as:
match '/create', :to => "content#create"
It's probably ugly but desperate times call for desperate measures. Just thought I'd share.
The idea is to make a 'redirect' while under the hood you generate a form with method :post.
I was facing the same problem and extracted the solution into the gem repost, so it is doing all that work for you, so no need to create a separate view with the form, just use the provided by gem function redirect_post() on your controller.
class MyController < ActionController::Base
...
def some_action
redirect_post('url', params: {}, options: {})
end
...
end
Should be available on rubygems.

Resources