unverified()->create(); $this->actingAs($user) ->get(route('dashboard')) ->assertRedirect(route('verification.notice')); }); it('lets a confirmed account through', function () { $this->actingAs(User::factory()->create()) ->get(route('dashboard')) ->assertOk(); }); it('sends the confirmation in this product’s design, not the framework’s', function () { // Laravel's own notification would arrive with its logo, its button and // its footer — as the first thing anybody ever receives from CluPilot. Mail::fake(); User::factory()->unverified()->create()->sendEmailVerificationNotification(); Mail::assertQueued(VerifyEmailMail::class); }); it('confirms the address when the signed link is opened', function () { $user = User::factory()->unverified()->create(); $this->actingAs($user)->get(verificationUrlFor($user))->assertRedirect(); expect($user->refresh()->hasVerifiedEmail())->toBeTrue(); }); it('refuses a link whose signature has been tampered with', function () { $user = User::factory()->unverified()->create(); $tampered = verificationUrlFor($user).'x'; $this->actingAs($user)->get($tampered)->assertForbidden(); expect($user->refresh()->hasVerifiedEmail())->toBeFalse(); }); it('refuses a link that has expired', function () { $user = User::factory()->unverified()->create(); $url = verificationUrlFor($user, 60); $this->travel(61)->minutes(); $this->actingAs($user)->get($url)->assertForbidden(); expect($user->refresh()->hasVerifiedEmail())->toBeFalse(); }); it('stops working once the address it was issued for has changed', function () { // The address is part of what is signed. Without that, a link mailed to an // address somebody has since corrected would still confirm the account — // confirming an address nobody can read. $user = User::factory()->unverified()->create(['email' => 'typo@example.com']); $url = verificationUrlFor($user); $user->update(['email' => 'correct@example.com']); $this->actingAs($user)->get($url)->assertForbidden(); expect($user->refresh()->hasVerifiedEmail())->toBeFalse(); }); it('will not let one account confirm another', function () { $mine = User::factory()->unverified()->create(); $theirs = User::factory()->unverified()->create(); $this->actingAs($mine)->get(verificationUrlFor($theirs))->assertForbidden(); expect($theirs->refresh()->hasVerifiedEmail())->toBeFalse(); }); it('throttles the resend button', function () { // Without it this is a button that makes the server send mail to an // arbitrary address as fast as it can be clicked. Mail::fake(); $user = User::factory()->unverified()->create(); $component = Livewire\Livewire::actingAs($user)->test(App\Livewire\Auth\VerifyEmail::class); foreach (range(1, 6) as $ignored) { $component->call('resend'); } Mail::assertQueuedCount(6); $component->call('resend'); Mail::assertQueuedCount(6); }); it('sends nobody back to the waiting room once they are through', function () { $this->actingAs(User::factory()->create()) ->get(route('verification.notice')) ->assertRedirect(route('dashboard')); }); /** The signed link the mail carries, built the same way VerifyEmailMail builds it. */ function verificationUrlFor(User $user, int $minutes = 60): string { return URL::temporarySignedRoute('verification.verify', now()->addMinutes($minutes), [ 'id' => $user->getKey(), 'hash' => sha1($user->getEmailForVerification()), ]); }