How to follow redirects inside test cases? - scala

Based on: Testing a page that is loaded after a redirect
Play!2.0 Framework (Scala) does have API which we can use to make redirects. However is there any way in Play Framework ( Scala ) that the test case automatically follows redirect responses. I would not want to repeat the redirect logic every time.
How to follow redirects inside test cases for Play Framework ( Scala ) ?

Edit: To make it completely clear. There isn't a built-in way to do this, and for a good reason. Unit tests are supposed to be simple and isolated. A single unit test is meant to test a single piece of functionality.
Example: You make a GET request to a page that needs authentication, but you provide no credentials, so the server responds with SEE OTHER and a flash message attached. You then make sure it responded to that request appropriately. Test over. The controller function that responds with SEE OTHER should have no knowledge of the other, other than maybe it'll do something with that flash data.
Example 2: You make a GET request to a login page with flash data attached. The server responds with OK and an HTML view displaying the flash message within it. You make sure it does so.
If you really want to tie these two together, then perhaps tests using WithBrowser would suit you more.
If you're testing redirect information, it's better to isolate it from whatever controller function/view the redirect is pointing to. There are test helpers you can use to achieve this.
Let's say you want to test
Redirect("/login").flashing("message" -> "You are not logged in.")
In your test you could use these helpers:
import play.api.test._
val request = FakeRequest(GET, "/secretpage")
val Some(result) = route(request)
status(result) must equalTo(SEE_OTHER)
redirectLocation(result) must beSome("/login")
flash(result).get("message") must beSome("You are not logged in.")
Note that this is for Play 2.1.x and above. If you're using 2.0.x, you should change route to routeAndCall.

Related

FlowRouter Reload Doesn't Route

I'm using FlowRouter. If I start on the homepage everything works well. I can work through the routes (change the pages) without problem. However, if I hit refresh in the browser, I get a series of errors. My url looks like this:
/story/586d536e34821281735b53a4
The ID is being returned in console under the following method:
Tracker.nonreactive(function(){
I think the subscription is being completed, so I'm a little confused as to why reloading a url is different than loading from the home page.
What am I not understanding here?
Reloading a url will make a HTTP request to server to get all the application source. Whereas navigating to a route from another one does not make any HTTP requests to get the application source because they are already available (they were loaded from the previous route), in this case the router will just get the appropriate content and render on the page. This is normal behaviour for Meteor apps and all other single-page apps
The error you encounter is because your data is not yet available on client, to fix it you could simple use a placeholder if the value is undefined.

Play Framework: Don't change URL after form validation failed

In a plain Play application I have the following scenario.
A route file which looks like this:
GET /accounts/add controllers.Accounts.add()
POST /accounts controllers.Accounts.create()
The first route results in a view where I can add a new account. The form to submit the new account looks something like this:
#helper.form(action = routes.Accounts.create()) {...}
Now the controller binds the input to the form and checks for any validation errors:
public static Result create() {
Form<Account> form = Form.form(Account.class).bindFromRequest();
if (form.hasErrors()) {
return badRequest(views.html.account.add.render(form));
}
...
}
Now the thing is, the client will see the same view with some additional error messages. However, meanwhile the URL has changed from http://example.com/accounts/add to http://example.com/accounts.
If the client now reloads the browser this calls GET http://example.com/accounts (which isn't even mapped in this scenario - thus getting a 404 - Not Found).
Maybe it's just me but I find this kind of annoying and browsing some GitHub projects I couldn't find a good solution for this case.
Of cause things would be much simpler if the second route is rewritten to:
POST /accounts/add controllers.Accounts.create()
... in which case everything works fine. But from a REST point of view this doesn't feel good either. The same applies to update scenarios (having GET /accounts/:id/update vs. PUT /accounts/:id).
Is there a guide on how to handle this? Am I getting something wrong or is this no problem at all (from a pragmatic point of view)?
It's not possible to leave the previous URL because a request for a new address has already been made. A controller only provides response for a requested resource. To go to the previous URL you could only make a redirect in case of validation failure but you would lost errors that way so this is not a solution.
I suggest mapping both actions with the same URL. This way you would solve problem with the browser reload.
If you create a REST service for http clients that aren't browsers you will probably want to serve different response than a simple http page. The separation of actions for particular clients could be a good solution for keeping REST API clean and a browser user happy.

Testing scala Play (2.2.1) controllers with CSRF protection

I've been having some problems testing controllers that use Play's CSRF protection. To demonstrate this, I've created a very simple Play application that minimally exhibits the problem.
https://github.com/adamnfish/csrftest
The full details are on the README of that repository, but to summarise here:
Consider a controller that is designed to handle a form submission. It has a GET method that uses CSRFAddToken and a POST method that uses CSRFCheck. The former adds a CSRF Token to the request so that a form field can be put in the rendered view, containing the valid token. When that form is submitted, if the CSRF check passes and the submission is valid, something else will happen (typically a redirect). If the form submission is not valid, the form submission is re-shown along with any errors so the user can correct the form and submit again.
This works great!
However, in the tests we now have some problems. To test the controller you can pass a fake request to it in the test. The CSRF check itself can be skipped by adding the nocheck header to the fake request but the view cannot be rendered because no token available to generate the form field. The test fails with a RuntimeException, "Missing CSRF Token (csrf.scala:51)".
Given that it works when it's actually running but not in the tests, it seems like this must be a problem with the way FakeRequests are run in Play tests but I may be doing something wrong. I've implemented the CSRF protection as described at http://www.playframework.com/documentation/2.2.1/ScalaCsrf and the testing as described at http://www.playframework.com/documentation/2.2.1/ScalaFunctionalTest. I'd appreciate any pointers if anyone has managed to test CSRF protected forms.
One solution is to test using a browser, eg Fluentlenium, as this will manage cookies etc, so the CSRF protection should all just work.
The other solution is to add a session to the FakeRequest so that it contains a token, eg:
FakeRequest().withSession("csrfToken" -> CSRF.SignedTokenProvider.generateToken)
Obviously if you're doing that a lot, you can create a help method to do that for you.
Bonus answer for those interested in Java: I got this to work in the Java version of Play Framework 2.2 by adding
.withSession(CSRF.TokenName(), CSRFFilter.apply$default$5().generateToken())
to fakeRequest()
Following on from #plade, I added a helper method to my base test class:
protected static FakeRequest csrfRequest(String method, String url) {
String token = CSRFFilter.apply$default$5().generateToken();
return fakeRequest(method, url + "?csrfToken=" + token)
.withSession(CSRF.TokenName(), token);
}
To those that are still interested: I managed to solve this problem globally by enabling CSRF protection in tests. The app will then create a token for every request that does not contain one. See my answer to this question
For those who might be interested, I created a trait for play 2.5.x :
https://stackoverflow.com/a/40259536/3894835
You can then use it in your tests requests like the addToken{} of the controller :
val fakeRequest = addToken(FakeRequest(/* params */))
I use the following method in my base integration test class:
def csrfRequest(method: String, uri: String)(implicit app: Application): FakeRequest[AnyContentAsEmpty.type] = {
val tokenProvider: TokenProvider = app.injector.instanceOf[TokenProvider]
val csrfTags = Map(Token.NameRequestTag -> "csrfToken", Token.RequestTag -> tokenProvider.generateToken)
FakeRequest(method, uri, FakeHeaders(), AnyContentAsEmpty, tags = csrfTags)
}
Then you can use it in your tests where you would use FakeRequest.

Redirecting requests form a catch-all controller in Zend Application without looping forever

There are plenty of related posts to what I'm asking, but after some lengthy searches couldn't quite find what I was looking for, my apologies if it exists somewhere.
My goal -- ALL requests to my Zend App must go through a preDispatch plugin, then pass to a custom Auth controller that will decide whether existing auth credentials are sufficient for the requested operation. 'Sufficient' depends on the logic of the app, hence why I want to do this at the controller+model level. If they suffice, they send the original request along to the specified controller+action, if not they default to a 'get lost' action.
At present I'm using an auth custom plugin set in the preDispatch to simply check for POST'ed auth credentials (if we are logging in), then in all cases the plugin stores the original request and redirects everyone (auth'd or not) to my auth controller, a-la:
$request->setModuleName('default')
->setControllerName('auth')
->setActionName('check')
->setParam('oreq',$request->getParams());
My problem/question is that within my auth->check action, how should I perform the redirect after a decision is made? If I use:
$this->_helper->redirector($or['action'], $oreq['controller']);
then I obviously get an infinite loop as these requests pass through the preDispatch plugin again. Sure I could pass something with the redirect so that the Auth plugin ignores such requests, but this is clearly a security hole. I had thought about maybe generating and storing an md5 hash, storing it to session and passing that as an escape param, but that seems a little sketchy.
Any better ideas out there? Perhaps a redirect method that does not go through the standard predispatch routine in Zend App? Thanks in advance!
This is not how it is done usually in Zend Framework. Not all requests go to a common place and gets redirected to the original requested place authentication.
For access control, use Zend_Acl. Through that, you could easily determine whether the current user has the necessary auth to access the content, else redirect to 'get lost' action.
If you are still adamant on using your technique, use _forward method instead of redirect method.
Since _forward is an internal redirect, you could pass additional arguments and check that in preDispath to avoid a loop.
$this->_forward($action, $controller, $module, $params)

Why GWT URL doesn't change on an event or a service call?

I have two questions:
Q: 1
I'm currently developing a GWT app. The entry point for the app is: ImageViewer.java. I could well access it by http://127.0.0.1:8888/ImageViewer.html?gwt.codesvr=127.0.0.1:9997. I have a service called "Search" which has corresponding "Async" and "Impl"'s defined. Now, I call the service from client side, using RPC. I could call the service, obtain return value. Everything works fine.
However, I expect the application to show a behavioral change on URL. i.e. when a service is being accessed, I thought it would be reflected on the browser's URL something like: http://127.0.0.1:8888/search?gwt.codesvr=127.0.0.1:9997 as I've modified web.xml. However, this behavior is not realized. Any particular reason why this is not reflected??
Q:2
This one is a reverse of the previous ques. i.e. I have an application running. Let's say it has an entrypoint class(Imageviewer.java) and another composite class (searchClass.java) which would be loaded on the Imageviewer based on an event. This searchClass invokes the "search" service mentioned in the previous question.
I could load the "searchClass" in "Imageviewer", invoke the service, and the service also returns the value needed. Everything works fine... But,
I need something like this: by just typing this query string:
http://127.0.0.1:8888/search?value=John
I want the "searchClass" to be loaded on the "ImageViewer", call the service using the value(which is "john" in this case) and display the result. Is this possible at all?
what I've tried: I have tried to create a httpServletClass on the server and mapped it with the URL and could do the search. The search returns appropriate results. However, I want the results from the server to be displayed on the client. Remember, I'm directly using a servlet to read the URL and so there is no value being passed from client to server.
Thanks in advance.
A: 1. To change URL, the hash part, you need to set new history token in the History class. More about history management in this article.
A: 2. For the second part you could achieve it by changing the history token, for instance "http://127.0.0.1/search#value=John". The history service will trigger an event if the # part changes. You could also use the part with "?", as in your example, if you use Window.Location , but it will cause reload of the application, which would put the whole idea of using GWT in question.
RPC (AJAX) calls are done Via XHR and do not change the browser URL.
You can't (with the URL you presented). GWT apps normally run in one web page, i.e. the URL does not change (see how gmail changes browser url bar). What you can do is enable GWT history support. Then your url would be http://host/#search?value=queryu