I am trying to enable functional testing for custom Devise routes, but I keep hitting roadblocks that I am not sure how to solve. The routes I am trying to test are in the devise_scope :user block of my routes file (see below)
devise_for :users, :controllers => {:registrations => "registrations",
:confirmations => "confirmations",
:sessions => "sessions"}
devise_scope :user do
get '/trainer/:referral_trainer' => "registrations#new"
get '*referral_trainer' => "registrations#new_client"
post '/client_sign_up' => "registrations#create_client"
end
and my tests look like:
test "should get new" do
params = {}
params["referral_trainer"] = "trainer"
get("new", params)
assert_response :success
params = {}
params["trainer"] = "trainer"
get("new_client", params)
assert_response :success
end
But the error(s) I get are:
ActionController::RoutingError: No route matches {:referral_trainer=>"trainer", :controller=>"registrations", :action=>"/trainer/*referral_trainer"}
ActionController::RoutingError: No route matches {:trainer=>"trainer", :controller=>"registrations", :action=>"new_client"}
In my test I also have:
setup do
#controller = RegistrationsController.new
#request.env["devise.mapping"] = Devise.mappings[:user]
end
So that the right controller and Devise mappings are being used.
Rake routes returns:
GET /trainer/:referral_trainer(.:format) registrations#new
GET /*referral_trainer(.:format) registrations#new_client
So I know the routes exist (and have used them in production), but I can not figure out how to test them programmatically.
Any help would be appreciated.
Thanks!
Related
I have a few links that are breaking. One, my logout, which I am using the delete method with, returns this error:
[Devise] Could not find devise mapping for path "/users/sign_out". This may happen for two reasons: 1) You forgot to wrap your route inside the scope block. For example: devise_scope :user do get "/some/route" => "some_devise_controller" end 2) You are testing a Devise controller bypassing the router. If so, you can explicitly tell Devise which mapping to use: #request.env["devise.mapping"] = Devise.mappings[:user]
I have this in my routes: get '/users/sign_out', to: 'devise/sessions#destroy'
And my devise routes look like this:
devise_for :users, controllers: { sessions: 'sessions',
registrations: 'registrations',
invitations: 'invitations' }
Why is this breaking?
I have this in my routes: get '/users/sign_out', to: 'devise/sessions#destroy'
if you want to allow the user to sign out via GET method all you have to do is go to app/config/initializers/devise.rb and uncomment the line config.sign_out_via = :get
OR Try this
devise_scope :user do
get '/users/sign_out', to: 'devise/sessions#destroy'
end
So I keep getting the error:
No route matches {:action=>"create", :controller=>"xaaron/api_keys"}
Which is thrown in the test:
it "should not create an api key for those not logged in" do
post :create
expect(response).to redirect_to xaaron.login_path
end
when I go to spec/dummy and run the rake routes command I see:
api_keys GET /api_keys(.:format) xaaron/api_keys#index
POST /api_keys(.:format) xaaron/api_keys#create
new_api_key GET /api_keys/new(.:format) xaaron/api_keys#new
edit_api_key GET /api_keys/:id/edit(.:format) xaaron/api_keys#edit
api_key GET /api_keys/:id(.:format) xaaron/api_keys#show
PATCH /api_keys/:id(.:format) xaaron/api_keys#update
PUT /api_keys/:id(.:format) xaaron/api_keys#update
DELETE /api_keys/:id(.:format) xaaron/api_keys#destroy
Which shows that yes this route does exist. My routes file for this engine looks like:
Xaaron::Engine.routes.draw do
get 'login' => 'sessions#new', :as => 'login'
get 'logout' => 'sessions#destroy', :as => 'logout'
get 'signup' => 'users#new', :as => 'signup'
get 'permission_denied' => 'error#denied', :as => 'permission_denied'
get 'record_not_found' => 'error#error', :as => 'record_not_found'
get 'password_reset' => 'password_resets#edit', :as => 'rest_user_password'
resource :error, controller: 'error'
resources :users
resources :api_keys
resources :sessions
resources :roles
resources :password_resets
end
What am I missing?
update
For those of you curious how I am getting these routes, its because the dummy app's routes file is set up (by default) as such:
Rails.application.routes.draw do
mount Xaaron::Engine => "/xaaron"
end
Update II
I have been reading this api docs on how routing is done in engines and I believe the way I have done this is correct, how ever the controller is defined as such:
module Xaaron
class ApiKeysController < ActionController::Base
before_action :authenticate_user!
def index
#api_key = Xaaron::ApiKey.where(:user_id => current_user.id)
end
def create
#api_key = Xaaron::ApiKey.new(:user_id => current_user.id, :api_key => SecureRandom.hex(16))
create_api_key(#api_key)
end
def destroy
Xaaron::ApiKey.find(params[:id]).destroy
flash[:notice] = 'Api Key has been deleted.'
redirect_to xarron.api_keys_path
end
end
end
You need to tell your spec you are using the engine routes:
describe ApiKeysController do
routes { Xaaron::Engine.routes }
it "should not create an api key for those not logged in" do
post :create
expect(response).to redirect_to xaaron.login_path
end
end
I'm trying to add a custom action to Devise's registration controller which allows users to change their passwords (I cannot use default registrations#edit since I need a form for changing only password, not email/username). The implementation I wrote below works in development mode but I get this error when I test controller.
Failure/Error: get 'password'
ActionController::RoutingError:
No route matches {:controller=>"registrations", :action=>"password"}
There is my code (I tried to skip unrelated parts)
spec/controllers/registrations_controller_spec.rb
describe RegistrationsController do
include Devise::TestHelpers
before :each do
request.env["devise.mapping"] = Devise.mappings[:users]
end
describe "GET 'password'" do
it "..." do
# The problem is here,
get 'password' # it raises ActionController::RoutingError
end
end
end
app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
# ...
def password
end
end
config/routes.rb
devise_for :users, path: 'account',
controllers: { registrations: 'registartions' },
skip: [:registartions, :sessions]
devise_scope :user do
# ...
scope '/account' do
get 'password' => 'devise/registartions#password', as: "change_password
do
end
spec_helper.rb
# ...
RSpec.configure do |config|
# ...
config.include Devise::TestHelpers, :type => :controller
end
I would usually add a comment for this, but I'm including a code block and it gets messy in comments.
It seems like you're trying to preform a GET on /password instead of on /account/password.
From what I'm reading, you've got a mapping for /account/password:
devise_scope :user do # used to remove /users/ part from devise URLs
# ...
scope '/account' do # adds /account to URLs
get 'password' => 'devise/registartions#password', as: "change_password"
# this will match /account/passwordAnswer
do
end
So you should either remove the scope, or replace your get request in test with this:
get "/account/password", :user => #user
or this
get change_password_path(#user)
Where #user is a User's mock.
Run rake routes to confirm.
Have you set up your config/routes.rb with the following.
devise_for :users do
get 'logout' => 'devise/sessions#destroy'
get 'changepassword' => 'devise/registrations#edit'
get 'access' => 'homepages#access'
get 'history' => 'policies#history'
get 'future' => 'policies#future'
end
devise_for :users, :controllers => { :sessions => :sessions }
resources :users
I have the same problem. I set the routes then it was worked for me :)
I don't like using get "/account/password" or a path in my spec. Seems hacky. I fixed a similar problem by using better syntax in my routes.rb file:
Using path: 'account' option in devise_for
Explicitly setting my custom controller with controllers: {registrations: 'registrations'}
So my routes.rb looks like this:
devise_for :users,
path: 'account',
path_names: {sign_in: 'login', sign_out: 'logout', sign_up: 'register'},
controllers: {registrations: 'registrations'},
skip: [:passwords]
Then I can use get :new in my test. Much cleaner.
Hope this helps someone else.
I am using devise 3.0.
This question has probably been asked a dozen times on Stack Overflow (e.g. (1), (2), (3), (4), (5)) but every time the answer seems to be different and none of them have helped me. I'm working on a Rails Engine and I'm finding that Rspec2 gets route errors, but I can reach the routes in the browser. Here's the situation:
In the engine's routes.rb:
resources :mw_interactives, :controller => 'mw_interactives', :constraints => { :id => /\d+/ }, :except => :show
# This is so we can build the InteractiveItem at the same time as the Interactive
resources :pages, :controller => 'interactive_pages', :constraints => { :id => /\d+/ }, :only => [:show] do
resources :mw_interactives, :controller => 'mw_interactives', :constraints => { :id => /\d+/ }, :except => :show
end
Excerpted output of rake routes:
new_mw_interactive GET /mw_interactives/new(.:format) lightweight/mw_interactives#new {:id=>/\d+/}
...
new_page_mw_interactive GET /pages/:page_id/mw_interactives/new(.:format) lightweight/mw_interactives#new {:id=>/\d+/, :page_id=>/\d+/}
And my test, from one of the controller specs (describe Lightweight::MwInteractivesController do):
it 'shows a form for a new interactive' do
get :new
end
...which gets this result:
Failure/Error: get :new
ActionController::RoutingError:
No route matches {:controller=>"lightweight/mw_interactives", :action=>"new"}
...and yet when I go to that route in the browser, it works exactly as intended.
What am I missing here?
ETA: To clarify a point Andreas raises: this is a Rails Engine, so rspec runs in a dummy application which includes the engine's routes in a namespace:
mount Lightweight::Engine => "/lightweight"
...so the routes shown in rake routes are prefaced with /lightweight/. That's why the route shown in the Rspec error doesn't seem to match what's in rake routes. But it does make the debugging an extra step wonkier.
ETA2: Answering Ryan Clark's comment, this is the action I'm testing:
module Lightweight
class MwInteractivesController < ApplicationController
def new
create
end
...and that's it.
I found a workaround for this. Right at the top of the spec, I added this code:
render_views
before do
# work around bug in routing testing
#routes = Lightweight::Engine.routes
end
...and now the spec runs without the routing error. But I don't know why this works, so if someone can post an answer which explains it, I'll accept that.
I think the might be something wrong higher up in you specs
how did the "lightweight" get into this line :controller=>"lightweight/mw_interactives"
the route says
new_mw_interactive GET /mw_interactives/new(.:format)
not
new_mw_interactive GET /lightweight/mw_interactives/new(.:format)
add a file spec/routing/root_routing_spec.rb
require "spec_helper"
describe "routes for Widgets" do
it "routes /widgets to the widgets controller" do
{ :get => "/" }.should route_to(:controller => "home", :action => "index")
end
end
then add a file spec/controllers/home_controller_spec.rb
require 'spec_helper'
describe HomeController do
context "GET index" do
before(:each) do
get :index
end
it {should respond_with :success }
it {should render_template(:index) }
end
end
I'm making a simple test project to prepare myself for my test.
I'm fairly new to nested resources, in my example I have a newsitem and each newsitem has comments.
The routing looks like this:
resources :comments
resources :newsitems do
resources :comments
end
I'm setting up the functional tests for comments at the moment and I ran into some problems.
This will get the index of the comments of a newsitem. #newsitem is declared in the setup ofc.
test "should get index" do
get :index,:newsitem_id => #newsitem
assert_response :success
assert_not_nil assigns(:newsitem)
end
But the problem lays here, in the "should get new".
test "should get new" do
get new_newsitem_comment_path(#newsitem)
assert_response :success
end
I'm getting the following error.
ActionController::RoutingError: No route matches {:controller=>"comments", :action=>"/newsitems/1/comments/new"}
But when I look into the routes table, I see this:
new_newsitem_comment GET /newsitems/:newsitem_id/comments/new(.:format) {:action=>"new", :controller=>"comments"}
Can't I use the name path or what I'm doing wrong here?
Thanks in advance.
The problem is in the way your test specifies the URL. The error message is:
No route matches {:controller=>"comments", :action=>"/newsitems/1/comments/new"}
and of course there is no action called "/newsitems/1/comments/new". You want to pass the hash { :controller => :comments, :action => :new, :news_item_id => 1 }.
The right syntax is simply:
get :new, :news_item_id => 1
(Assuming Rails 3)
Try this in your routes.rb
GET 'newsitems/:newsitem_id/comments/new(.:format)' => 'comments#new', :as => :new_newsitem_comment