Skip to content

Commit

Permalink
Add tests for email changing
Browse files Browse the repository at this point in the history
  • Loading branch information
danharrin committed Dec 31, 2024
1 parent 2c2b044 commit e651c62
Show file tree
Hide file tree
Showing 12 changed files with 420 additions and 0 deletions.
2 changes: 2 additions & 0 deletions tests/src/Fixtures/Providers/AdminPanelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ public function panel(Panel $panel): Panel
->login()
->registration()
->passwordReset()
->emailChangeVerification()
->emailVerification()
->profile()
->resources([
DepartmentResource::class,
PostResource::class,
Expand Down
4 changes: 4 additions & 0 deletions tests/src/Fixtures/Providers/SlugsPanelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ public function panel(Panel $panel): Panel
->passwordResetRoutePrefix('password-reset-test')
->registration()
->registrationRouteSlug('register-test')
->emailChangeVerification()
->emailChangeVerificationRouteSlug('verify-change-test')
->emailChangeVerificationRoutePrefix('email-change-verification-test')
->emailVerification()
->emailVerificationPromptRouteSlug('prompt-test')
->emailVerificationRouteSlug('verify-test')
->emailVerificationRoutePrefix('email-verification-test')
->profile()
->resources([])
->pages([])
->middleware([
Expand Down
197 changes: 197 additions & 0 deletions tests/src/Panels/Auth/EditProfileTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
<?php

use Filament\Auth\Notifications\NoticeOfEmailChangeRequest;
use Filament\Auth\Notifications\VerifyEmailChange;
use Filament\Auth\Pages\EditProfile;
use Filament\Facades\Filament;
use Filament\Tests\Fixtures\Models\User;
use Filament\Tests\TestCase;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Str;
use League\Uri\Components\Query;

use function Filament\Tests\livewire;

uses(TestCase::class);

beforeEach(function () {
$this->user = User::factory()->create();

$this->actingAs($this->user);
});

it('can render page', function () {
$this->get(Filament::getProfileUrl())
->assertSuccessful();
});

it('can retrieve data', function () {
livewire(EditProfile::class)
->assertFormSet([
'name' => $this->user->name,
'email' => $this->user->email,
]);
});

it('can save name', function () {
$newUserData = User::factory()->make();

livewire(EditProfile::class)
->fillForm([
'name' => $newUserData->name,
])
->call('save')
->assertHasNoFormErrors()
->assertNotified('Saved');

expect($this->user->refresh())
->name->toBe($newUserData->name);
});

it('can save email', function () {
Filament::getCurrentOrDefaultPanel()->emailChangeVerification(false);

$newUserData = User::factory()->make();

livewire(EditProfile::class)
->fillForm([
'email' => $newUserData->email,
'currentPassword' => 'password',
])
->call('save')
->assertHasNoFormErrors()
->assertNotified('Saved');

expect($this->user->refresh())
->email->toBe($newUserData->email);
});

it('can send email change verification', function () {
Notification::fake();

Filament::getCurrentOrDefaultPanel()->emailChangeVerification();

$oldEmail = $this->user->email;

$newUserData = User::factory()->make();

livewire(EditProfile::class)
->fillForm([
'email' => $newUserData->email,
'currentPassword' => 'password',
])
->call('save')
->assertHasNoFormErrors()
->assertNotified('Email address change request sent')
->assertFormSet([
'email' => $oldEmail,
]);

expect($this->user->refresh())
->email->toBe($oldEmail);

Notification::assertSentTo($this->user, NoticeOfEmailChangeRequest::class, function (NoticeOfEmailChangeRequest $notification) use ($newUserData): bool {
if (blank($notification->blockVerificationUrl)) {
return false;
}

return $notification->newEmail === $newUserData->email;
});

Notification::assertSentOnDemand(VerifyEmailChange::class, function (VerifyEmailChange $notification): bool {
$verificationSignature = Query::new($notification->url)->get('signature');

return cache()->has($verificationSignature);
});
});

it('can save password', function () {
expect(Filament::auth()->attempt([
'email' => $this->user->email,
'password' => 'password',
]))->toBeTrue();

$newPassword = Str::random();

livewire(EditProfile::class)
->fillForm([
'password' => $newPassword,
'passwordConfirmation' => $newPassword,
'currentPassword' => 'password',
])
->call('save')
->assertHasNoFormErrors()
->assertNotified('Saved')
->assertFormSet([
'password' => '',
'passwordConfirmation' => '',
]);

expect(Filament::auth()->attempt([
'email' => $this->user->email,
'password' => 'password',
]))->toBeFalse();

expect(Filament::auth()->attempt([
'email' => $this->user->email,
'password' => $newPassword,
]))->toBeTrue();
});

it('can validate', function (array $formData, array $errors) {
Filament::getCurrentOrDefaultPanel()->emailChangeVerification(false);

livewire(EditProfile::class)
->fillForm($formData)
->call('save')
->assertHasFormErrors($errors)
->assertNotNotified('Saved');

$this->user->refresh();

foreach ($formData as $key => $value) {
expect($this->user->getAttributeValue($key))
->not->toBe($value);
}
})->with([
'`name` is required' => [
['name' => '', 'email' => fake()->email],
['name' => ['required']],
],
'`name` is max 255 characters' => [
['name' => Str::random(256), 'email' => fake()->email],
['name' => ['max']],
],
'`email` is required' => [
['name' => fake()->name, 'email' => ''],
['email' => ['required']],
],
'`email` is valid email address' => [
['name' => fake()->name, 'email' => 'not-an-email'],
['email' => ['email']],
],
'`email` is unique' => fn (): array => [
['name' => fake()->name, 'email' => User::factory()->create()->email],
['email' => ['unique']],
],
'`password` is confirmed' => fn (): array => [
['name' => fake()->name, 'email' => fake()->email, 'password' => Str::random(), 'passwordConfirmation' => Str::random()],
['password' => ['same']],
],
'`passwordConfirmation` is required when `password` is filled' => fn (): array => [
['name' => fake()->name, 'email' => fake()->email, 'password' => Str::random(), 'passwordConfirmation' => ''],
['passwordConfirmation' => ['required']],
],
'`currentPassword` is required when `password` is filled' => fn (): array => [
['name' => fake()->name, 'email' => fake()->email, 'password' => Str::random(), 'currentPassword' => ''],
['currentPassword' => ['required']],
],
'`currentPassword` is required when `email` is changed' => fn (): array => [
['name' => fake()->name, 'email' => fake()->email, 'currentPassword' => ''],
['currentPassword' => ['required']],
],
'`currentPassword` is valid password' => fn (): array => [
['name' => fake()->name, 'email' => fake()->email, 'currentPassword' => 'invalid-password'],
['currentPassword' => ['current_password']],
],
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

use Filament\Facades\Filament;
use Filament\Notifications\Notification;
use Filament\Tests\Fixtures\Models\User;
use Filament\Tests\TestCase;
use League\Uri\Components\Query;

uses(TestCase::class);

it('can block an email change', function () {
$userToVerify = User::factory()->create();
$newEmail = fake()->email;

expect($userToVerify->refresh())
->email->not->toBe($newEmail);

$verificationUrl = Filament::getVerifyEmailChangeUrl($userToVerify, $newEmail);

$verificationSignature = Query::new($verificationUrl)->get('signature');
cache()->put($verificationSignature, true, ttl: now()->addHour());

$blockVerificationUrl = Filament::getBlockEmailChangeVerificationUrl($userToVerify, $newEmail, $verificationSignature);

$this
->actingAs($userToVerify)
->get($blockVerificationUrl)
->assertRedirect(Filament::getProfileUrl());

Notification::assertNotified('Email address change blocked');

expect($userToVerify->refresh())
->email->not->toBe($newEmail);

expect(cache()->has($verificationSignature))
->toBeFalse();

$this
->actingAs($userToVerify)
->get($verificationUrl)
->assertForbidden();

expect($userToVerify->refresh())
->email->not->toBe($newEmail);
});

it('cannot block an email change when signed in as another user', function () {
$userToVerify = User::factory()->create();
$newEmail = fake()->email;

$verificationUrl = Filament::getVerifyEmailChangeUrl($userToVerify, $newEmail);

$verificationSignature = Query::new($verificationUrl)->get('signature');
cache()->put($verificationSignature, true, ttl: now()->addHour());

$blockVerificationUrl = Filament::getBlockEmailChangeVerificationUrl($userToVerify, $newEmail, $verificationSignature);

$anotherUser = User::factory()->create();

$this
->actingAs($anotherUser)
->get($blockVerificationUrl)
->assertForbidden();

$this
->actingAs($userToVerify)
->get($verificationUrl)
->assertRedirect(Filament::getProfileUrl());

expect($userToVerify->refresh())
->email->toBe($newEmail);
});

it('cannot block an email change once it has been verified', function () {
$userToVerify = User::factory()->create();
$newEmail = fake()->email;

$verificationUrl = Filament::getVerifyEmailChangeUrl($userToVerify, $newEmail);

$verificationSignature = Query::new($verificationUrl)->get('signature');
cache()->put($verificationSignature, true, ttl: now()->addHour());

$this
->actingAs($userToVerify)
->get($verificationUrl)
->assertRedirect(Filament::getProfileUrl());

expect($userToVerify->refresh())
->email->toBe($newEmail);

$blockVerificationUrl = Filament::getBlockEmailChangeVerificationUrl($userToVerify, $newEmail, $verificationSignature);

$this
->actingAs($userToVerify)
->get($blockVerificationUrl)
->assertRedirect(Filament::getProfileUrl());

Notification::assertNotified('Failed to block email address change');

expect($userToVerify->refresh())
->email->toBe($newEmail);
});
Loading

0 comments on commit e651c62

Please sign in to comment.