Laravel Dusk Sessions on - laravel

I have a laravel 5.4 project set up on a vagrant machine and I'm trying to create Laravel Dusk tests on it. It was a bit tricky and I followed the tutorial here : http://www.jesusamieiro.com/using-laravel-dusk-with-vagrant-homestead/
It runs fine, but the session variables don't seem to be saving. I have this code in a post route
if (!session('user_name')) {
$request->session()->flash("message", "Your session expired. Click "Please Confirm" to approve your monthly donation.");
return redirect()->back();
}
and it is set on the get request when the page is first fetched. I have this test in dusk:
$browser->visit("http://support.welcome.dev/?layout_id={$layout}&purl={$supporter->purl}&user_name={$supporter->name_first}&amount=10&ct={$hash}")
->press("Please Confirm")
->assertPathIs("/confirm");
This test fails and is redirected with the message session has expired. However, it works just fine when when the code in the post route checking for the user_name variable is commented out. The original code works when I test it manually on a browser.
Any help is appreciated.

$user = factory(User::class)->create([
'email' => 'taylor#laravel.com',
]);
$this->browse(function ($browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'secret')
->press('Login')
->assertPathIs('/home');
});
This code is write on https://laravel.com/docs/5.4/dusk. i think this can help you. =)

Related

Can i run php unit tests using Laravel Policy in route middleware?

I am using Laravel policies to control authorisation on my api routes. I want to only allow the current user to update their own post. This works fine when i manually run it through the application, but the unit tests fail. The unit tests, redirect to the login screen.
route:
Route::post('/posts/{post:reference}/editDetails', [PostDetailsApiController::class, 'update'])
->middleware('can:update,post');
policy:
public function update(User $user, Post $post)
{
return $post->user_id === $user->id;
}
unit test:
$this->user = User::factory()->create();
$post = Post::factory()->create(['user_id' => $this->user->id]);
Passport::actingAs($this->user);
$response = $this->call('POST', 'http://test/api/posts/' . $post->reference . '/editDetails');
$response->assertStatus(200);
but this test fails saying 'Failed asserting that 200 is identical to 302'. If i add followingRedirects() and actingAsClient then it passes. If i dd in the controller, it doesnt get fired, so i'm pretty sure the controller isnt getting hit? if i remove the middleware, it runs fine. any advice welcomed. thanks
This has nothing to do with the policy as the status code 302 is a redirect. The problem lies within this snippet.
$this->call('POST', 'http://test/api/posts/' . $post->reference . '/editDetails');
Normally you would call it with a relative URL.
$this->call('POST', 'api/posts/' . $post->reference . '/editDetails');
A better approach is to use the route helper for named routes. I have a example project, where i used this approach.
$this->call('POST', route('posts.edit-details', ['reference' => $post->reference]));
Remember to add a name to your route.
Route::post('/posts/{post:reference}/editDetails', [PostDetailsApiController::class, 'update'])
->middleware('can:update,post')
->name('posts.edit-details');

Laravel Dusk test error on Algolia Search: Impossible to connect, please check your Algolia Application Id

In this project we use Algolia Search installed through Composer. When I'm running a dusk test on the login form it fails because of an error. The login on the form itself performs wel, it seems when this test actually logs in and ends up on the homescreen, this is where it fails.
Note: There is an Algolia APP_ID and SECRET defined in the .env file, and all is working fine when using the application.
The actual error output for this test:
1) Tests\Browser\LoginTest::testLogin
Algolia\AlgoliaSearch\Exceptions\UnreachableException: Impossible to connect, please check your Algolia Application Id.
Dusk test:
public function testLogin()
{
$user = factory(User::class)->create([
'email' => 'dusktester#mail.com',
'password' => '***'
]);
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', 'dusktester#mail.com')
->type('password', '***!')
->press('.button')
->assertPathIs('/');
});
}
Solved it another way. Our login system is a little more complex and links another table depending on the type of user you are. Since this wasn't defined in my user factory within this Dusk test it lacked some crucial information about this user which led to the Algolia Search error.
The way I solved it:
No longer creating a user within the Dusk test and use one of my already seeded test users. The credentials for this user are taken from my .env file to ensure a clean / safe dusk test file that can be uploaded to Git:
public function testLogin()
{
$this->browse(function (Browser $browser){
$browser->visit('/login')
->type('email', env('DUSK_USER'))
->type('password', env('DUSK_PASSWORD'))
->press('.button')
->assertPathIs('/');
});
}

Acessing auth user attribute

I am in the guzzle controller making a request to an external api.
I wanna use an id from the user who is logged in.
I have been doing the request with a static id, but now i want it dynamically.
I tried like this:
$science = Auth::user()->science_id;
$client = new Client(['headers' => ['Accept' => 'application/json']]);
$request = $client->get(
'https://url_to_the_api/'.$science.'/degree',
[
'auth' => ['client', 'secret'],
]
);
$data = $request->getBody()->getContents();
return $data;
And i have the error
500(internal server error)
and this message:
"Trying to get property 'science_id' of non-object"
What am i missing?
Thanks for your time
If you are using it in web app then make sure you first check if user is already authenticated by using auth middleware or manually by using Auth::check() function.
Or
If you are trying to hit this by api that will not work here because session will not be maintained in that case. That's why JWT tokens were introduced to maintain the state of an application.
I've solved it like this:
$science = auth('api')->user()->science_id;
Thanks for the help!

Laravel 5 and Socialite - New Redirect After Login

Another newb question here, but hopefully someone can shed some light:
I am using Socialite with Laravel 5, and I want to be able to redirect the user to a page on the site after they have logged in. The problem is that using
return redirect('any-path-I-put-here');
simply redirects back to 'social-site/login?code=afkjadfkjdslkfjdlkfj...' (where 'social-site' is whatever site is being used i.e. facebook, twitter, google, etc.)
So, what appears to me to be happening is that the redirect() function in the Socialite/Contracts/Provider interface is overriding any redirect that I attempt after the fact.
Just for clarification, my routes are set up properly. I have tried every version of 'redirect' you can imagine ('to', 'back', 'intended', Redirect::, etc.), and the method is being called from my Auth Controller (though I have tried it elsewhere as well).
The question is, how do I override that redirect() once I am done storing and logging in the user with socialite? Any help is appreciated! Thank you in advance.
The code that contains the redirect in question is:
public function socialRedirect( $route, $status, $greeting, $user )
{
$this->auth->login( $user, true );
if( $status == 'new_user' ) {
// This is a new member. Make sure they see the welcome modal on redirect
\Session::flash( 'new_registration', true );
return redirect()->to( $route );// This is just the most recent attempt. It originated with return redirect($route);, and has been attempted every other way you can imagine as well (as mentioned above). Hardcoding (i.e., 'home') returns the exact same result. The socialite redirect always overrides anything that is put here.
}
else {
return redirect()->to( $route )->with( [ 'greeting' => $greeting ] );
}
}
... The SocialAuth class that runs before this, however, is about 500 lines long, as it has to determine if the user exists, register new users if necessary, show forms for different scenarios, etc. Meanwhile, here is the function that sends the information through from the Social Auth class:
private function socialLogin( $socialUser, $goto, $provider, $status, $controller )
{
if( is_null( $goto ) ) {
$goto = 'backlot/' . $socialUser->profile->custom_url;
}
if( $status == 'new_user' ) {
return $controller->socialRedirect($goto, $status, null, $socialUser);
}
else {
// This is an existing member. Show them the welcome back status message.
$message = 'You have successfully logged in with your ' .
ucfirst( $provider ) . ' credentials.';
$greeting =
flash()->success( 'Welcome back, ' . $socialUser->username . '. ' . $message );
return $controller->socialRedirect($goto, $status, $greeting, $socialUser);
}
}
I managed to workaround this problem, but I am unsure if this is the best way to fix it. Similar to what is stated in question, I got authenticated callback from the social media, but I was unable to redirect current response to another url.
Based on the callback request params, I was able to create and authenticate the user within my Laravel app. It worked good so far but the problems occured after this step when I tried to do a return redirect()->route('dashboard');. I tried all the flavours of redirect() helper and Redirect facade but nothing helped.
The blank page just stared at my face for over 2 days, before I checked this question. The behaviour was very similar. I got redirect from social-media to my app but could not further redirect in the same response cycle.
At this moment (when the callback was recieved by the app and user was authenticated), if I refreshed the page manually (F5), I got redirected to the intended page. My interpretation is similar to what's stated in this question earlier. The redirect from social-media callback was dominating the redirects I was triggering in my controller (May be redirect within Laravel app got suppressed because the redirect from social-media was still not complete). It's just my interpretation. Experts can throw more light if they think otherwise or have a better explaination.
To fix this I issued a raw http redirect using header("Location /dashboard"); and applied auth middleware to this route. This way I could mock the refresh functionality ,redirect to dashboard (or intended url) and check for authentication in my DashboardController.
Once again, this is not a perfect solution and I am investigating the actual root of the problem, but this might help you to move ahead if you are facing similar problem.
I believe you are overthinking this. Using Socialite is pretty straight forward:
Set up config/services.php. For facebook I have this:
'facebook' => [
'client_id' => 'your_fb_id',
'client_secret' => 'your_fb_secret',
'redirect' => '>ABSOLUTE< url to redirect after login', //like: 'http://stuff'
],
Then set up two routes, one for login and one for callback (after login).
In the login controller method:
return \Socialize::with('facebook')->redirect();
Then in the callback function
$fb_user = \Socialize::with('facebook')->user();
// check if user exists, create it and whatnot
//dd($fb_user);
return redirect()->route('some.route');
It should be pretty much similar for all other providers.
We are using the Socialite login in our UserController as a trait. We simply overrode the AuthenticatesSocialiteLogin::loginSuccess() in our controller.
use Broco\SocialiteLogin\Auth\AuthenticatesSocialiteLogin;
class UserController extends BaseController
{
use AuthenticatesSocialiteLogin;
public function loginSuccess($user)
{
return redirect()->intended(url('/#login-success'));
}
....

Laravel 4 Email not working

I've been looking for the answer for two days and still nothing. I've followed several tutorials where nothing more than just a few config settings are required, but my Laravel 4 app still doesn't want to send an e-mail. It always throws the same Exception and nobody seems to have the answer. It seems that I've tried everything but the working one.
Route::get('/sendemail', function() {
$data['user'] = 'Test User';
Mail::send('email.test', $data, function($m) {
$m->to('myemail#example.com')->subject('Email test');
});
});
But when I'm in the specified route, it always throws the same error:
Argument 1 passed to Swift_Transport_EsmtpTransport::__construct()
must implement interface Swift_Transport_IoBuffer, none given
The config is ok and even changing the driver from 'smtp' to 'mail' in config/mail.php throws also Exception.
Argument 1 passed to Swift_Transport_MailTransport::__construct() must be an instance of Swift_Transport_MailInvoker, none given
I'm stuck in here and I don't know that to do. My composer is up to date and PHP version is 5.4.4.
Any advice will be much appreciated.
Thank you.
You have probably found an answer by now.
But I think you need a second argument for the to.
Try:
$data = array('user' => $user);
Mail::send('email.test', $data, function($message) use ($user)
{
$message->to($user->email, $user->name)->subject('Email Test');
});

Resources