I'm attempted to test the mobile version of my rails site, but i can't seem to get the following code to work:
let(:uri) { '/' }
it 'routes to #mobile_index' do
get uri, :format => 'mobile'
controller.response.should route_to('home#index_mobile')
end
What's the proper way to send this sort of request so its seen by the app as coming from a mobile source? I've looked up a lot about setting the user agent, but i can't get any of those to work either. I'm using Rspec version 2.14.2.
How do you check if whether to redirect to mobile page or to the normal?
For this testcode to work you must be having something like this in your application#index
respond_to do |format|
format.mobile do
# redirect to mobile
end
format.html
end
This means if you call '/index' (or '/' ) and if you call '/index.mobile' it would be
redirecting to the mobile page
Because you've written something about the User Agent i guess this is your criterium for
distinguishing between mobile and normal version.
HTTP Headers in rails tests are set by the request.env method. Their names are prefixed
with HTTP_, capitalized and have dashes replaced by underscores.
so for setting the User-Agent header you just do
request.env['HTTP_USER_AGENT'] = "WWW-Mechanize"
and then perform the get call.
If you are checking only one and not multiple controllers in integration i would also make this a functional test of the Application Controller (or whatever controller responsible for the home action)
describe ApplicationController do
describe "GET index" do
it "redirects mobile agents to the mobile version" do
request.env['HTTP_USER_AGENT'] = 'Ipod ...'
#calls "/index" unless different routing configured
get :index
expect(response).to redirect_to <index_mobile_path> #path helper depends on route config
end
end
end
Related
This is my task
1) Create a new route localhost:3000/device
2) Consider if this url is hit from the mobile phones or desktop browsers, then
3) Track the system/device (iOS, android, web) from which the URL is hit
4) Based on the device from which the request has come, we need to redirect to some other URL (e.g., iOS ——> “iOS app store”, android ——> “Android play store”, web ——> “google page”)
5) Find what are the different approaches that are available to track the system from which the request has come and what would be the best one to implement and why?
Here I found a solution, but in rspec it causes error.
This is my route
get :devise, to: 'topics#devise'
And this is my controller
class TopicsController < ApplicationController
def devise
if request.env['HTTP_USER_AGENT'].downcase.match(/mac/i)
redirect_to 'https://itunes.apple.com/us/app/apple-store/id375380948?mt=8'
elsif request.env['HTTP_USER_AGENT'].downcase.match(/windows/i)
redirect_to 'https://www.microsoft.com/en-in/store/apps/windows'
elsif request.env['HTTP_USER_AGENT'].downcase.match(/android/i)
redirect_to 'https://play.google.com/store?hl=en'
else
redirect_to root_path
end
end
end
When I hit url lvh.me:3000/devise it redirects to the respective app store.
This is my controller spec
context 'devise' do
it 'should detect the device' do
get :devise
response.should redirect_to '/https://www.microsoft.com/en-in/store/apps/windows'
end
end
and this caused the error:
Expected response to be a redirect to http://test.host/https://www.microsoft.com/en-in/store/apps/windows but was a redirect to http://test.host/.
Expected "http://test.host/https://www.microsoft.com/en-in/store/apps/windows" to be === "http://test.host/".
If I did it in a wrong way, tell some suggestion for doing rspec
If your rails version is not too ancient in controller you can use request.user_agent (it will look into env anyway, but this makes code cleaner)
Browsers pass user agent in header User-agent (which in turn ends up in rack env), so you need to simulate this in your tests.
For testing this I'd recommend using request specs instead of controller ones (which are deprecated in rails 5):
RSpec.describe 'Topics...', type: :request do
it "redirects for ios" do
get '/your/topcis/path/here', headers: { 'HTTP_USER_AGENT' => 'iPhone' }
expect(response).to redirect_to(/\.apple\.com/)
end
end
(above uses rails 5, for older rails headers will be just hash, not a keyword argument)
Also you can write your method with case statement:
def devise
redirect_to case request.user_agent.downcase
when /mac/i then 'https://itunes.apple.com/us/app/apple-store/id375380948?mt=8'
when /windows/i then 'https://www.microsoft.com/en-in/store/apps/windows'
when /android/i then 'https://play.google.com/store?hl=en'
else
root_path
end
end
I have a rspec test testing a controller action.
class SalesController < ApplicationController
def create
# This redirect sends the user to SalesController#go_to_home
redirect_to '/go_to_home'
end
def go_to_home
redirect_to '/'
end
end
My controller test looks like
RSpec.describe SalesController, type: :controller do
include PathsHelper
describe 'POST create' do
post :create
expect(response).to redirect_to '/'
end
end
However, when I run the test it tells me that:
Expected response to be a redirect to <http://test.host/> but was a redirect to <http://test.host/go_to_home>.
Expected "http://test.host/" to be === "http://test.host/go_to_home".
/go_to_home will send the user to SalesController#go_to_home. How can I test that the response will eventually lead to the home page with the url http://test.host/?
Why are you expecting to be redirect to '/' in the specs?
From the controller code you pasted you are going to be redirected to '/go_to_home' after hitting the create action
Try changing the specs to:
expect(response).to redirect_to '/go_to_home'
Edit:
Is this a real example or the code is just for sharing what you are trying to achieve?
I don't think rspec will follow the redirect after going to '/go_to_home' and I think is fine.
If you are testing the create action it's ok to test that redirects to '/go_to_home' because that's what action is doing.
Then you can do another test for the other action go_to_home and expect that redirects to root.
Are you calling the action 'go_to_home' from somewhere else?
Controller tests are effectively unit tests - you are testing the effect of calling a single action and what the expected behaviour of that action is.
The create action does return a response back with a status code of 302 and includes in the header a Location indicating the new URI, which in the case of calling create would be Location: http://localhost/go_to_home
This is as far as the controller test goes. It has emulated a call made from a browser to the create action and received the initial redirection.
In the real world of course the browser would then navigate to the given location and would then hit the go_to_home action but this is beyond the scope of controller tests ... this is in the domain of integration testing.
So, either,
Create an integration test to initially call the create action, follow the redirection and test that you end up at '/'.
Change the controller test to expect(response).to redirect_to '/go_to_home'
Change the create action to redirect directly to '/'
Some third-party service which I want to use requires user to log in on their webpage. Handy url is generated in controller below. User goes there and come back to my service after his authentication succeeds
class MyController < App::BaseController
def login
redirect_to SOME_API.external_url(ENV['secret_id'])
end
end
As user comes back to my service he brings some feedback from third-party service in URL params (like: myapp.com/login_callack?response=error&reason=wrong_password). There are many variants of these params so I need to handle them in my action
def login_callback
#SOME MAGIC WITH COOKIES/ERRORS/What ever
redirect_to root_path
end
I want to write feature specs for it using RSpec and capybara.
scenario 'User authenticates with third-party thing' do
visit '/anything'
click_link 'Go to MyController#login and than get redirect to somerandom.url/login_logic'
# use cassette below
fill_out_form
click_button confirm
# magic happens on their side and their redirect user to my app into #login_callback action
expect(page).to have(/all fancy things i need/)
end
end
however calling WebMock.disable_net_connect!(allow_localhost: true) or adding vcr: { cassette_name: 'using_third_party_login', record: :new_episodes } doesn't prevent this scenario to being redirect to external url.
Capybara just let being redirected to external uri and no cassets are being recorded/played. Any idea how to stub redirect_to external_url with cassette play/record?
i'm having some issues trying to update an attribute outisde my web app (No route matches [GET] "/admin/justifications/19/approve").
The user should approve or reject a permission from their emails...
admin/justifications_controller.rb
class JustificationsController < BaseController
before_action :find_justification
# PATCH/PUT /admin/justifications/1/approve
def approve
#justification.approve
#justification.create_activity :approve, owner: current_user, recipient: #justification.user
redirect_to request.referer
end
# PATCH/PUT /admin/justifications/1/reject
def reject
#justification.reject
#justification.create_activity :reject, owner: current_user, recipient: #justification.user
redirect_to request.referer
end
routes
scope :admin, module: :admin do
resources :justifications, except: :all do
member do
patch :approve
patch :reject
end
end
...
end
Tho this works well in my web page, but it breaks when users try to open the generated links sent to their emails.
Is it something im missing here??
Any help would be great. Thnks!!
Your approve action is only available via PATCH or PUT and the link you press in your email sends the request via GET
There are lots of questions in SO asking how to send a different method than GET from the link you send in the email and the answer for that is: It is not possible. You have to open an GET action to be accessed from your email links.
There is no way to natively add links which send a PATCH request - Rails uses a data-method attribute together with some clever javascript to fake PATCH, PUT and DELETE requests.
However this only works if jquery-ujs is loaded in the client, which is problematic since many email clients and even webmail clients block emails from running javascript for security reasons.
What you need to do is add a GET route.
I have a model called Project, which is a collection of information stored by a Company. This company can create projects two ways.
The first is the standard RESTful way - the company goes to the Project index, then clicks 'New Project', and upon creation is returned to the index with a flash message.
The second is a 'quick create' that can be accessed when a company is looking at a client's profile. From here, the company can enter some basic information and send this off to create a project with that client (the client is specified automatically here).
The second of these two scenarios has a project being accessed from clients/show. Sending this data to projects/create would ordinarily route the company to projects/index, but I don't want that. In this case, the create action is meaningfully different in that certain fields are treated differently, and the redirect is also different. What would you suggest I do?
Build an alternative 'create_from_client' action in projects.
Build a 'create_project' action in clients.
Send a parameter to projects/create and set client_id and redirect to client/show if that parameter exists.
Something else I'm not aware of.
Thanks!
You can leverage the referrer directly from the Request object and fork based on that, similar to how redirect_to :back works.
From the Rails API docs for the redirect_to options hash:
:back - Back to the page that issued the request.
Useful for forms that are triggered from multiple places.
Short-hand for redirect_to(request.env["HTTP_REFERER"])
So you can simply do something like this:
def create
#project = Project.new( params[:project] )
#project.save
respond_with #project, location: get_location!
end
private
def get_location!
case request.env["HTTP_REFERER"]
# Your routing logic here.
end
This is nice and easy to unit test, too, if you're into that. :)
context "if the user came from the regular form" do
before { controller.request.env["HTTP_REFERER"] = "regular_form_url" }
it "redirects to the index path" do
post :create
response.should redirect_to :index
end
end
context "if the user came from the quick-create form" do
before { controller.request.env["HTTP_REFERER"] = "quick_create_url" }
it "redirects to some other path" do
post :create
response.should redirect_to some_other_path
end
end
I would just add another action to the controller, 'quick_create' or whatever. You can dry out the form with partials and parameters to the partial to tell how to render things...This just seems like the easiest way.
I've got this semi-rational (or is that semi-irrational) hang up against leveraging the referrer...
I ussualy add hidden referer field with current URL then redirect to it
For example
def create
#project = Project.new params[:project]
#project.save
respond_with #project, :location => params[:referer] || [:index, :projects]
end