dstpabuaran.com/tests/Feature/Settings/ProfileUpdateTest.php

717 lines
22 KiB
PHP

<?php
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Inertia\Testing\AssertableInertia as Assert;
uses(RefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| AUTHENTICATION
|--------------------------------------------------------------------------
*/
test('guests are redirected to the login page for profile edit', function () {
$response = $this->get(route('profile.edit'));
$response->assertRedirect(route('login'));
});
test('guests are redirected to the login page for profile update', function () {
$response = $this->patch(route('profile.update'), [
'email' => 'test@example.com',
'username' => 'testuser',
'full_name' => 'Test User',
]);
$response->assertRedirect(route('login'));
});
/*
|--------------------------------------------------------------------------
| PROFILE EDIT PAGE
|--------------------------------------------------------------------------
*/
test('profile page is displayed', function () {
$user = User::factory()->create();
$response = $this
->actingAs($user)
->get(route('profile.edit'));
$response->assertOk();
});
test('profile page renders correct inertia component', function () {
$user = User::factory()->create();
$this->actingAs($user)
->get(route('profile.edit'))
->assertInertia(fn (Assert $page) => $page
->component('settings/profile')
);
});
test('profile page passes user data to view', function () {
$user = User::factory()->create([
'email' => 'john@example.com',
'username' => 'johndoe',
]);
$user->userProfile()->create([
'full_name' => 'John Doe',
'phone_number' => '08123456789',
'gender' => 'male',
'birth_date' => '1990-05-15',
'address' => 'Jl. Sudirman No. 1',
]);
$this->actingAs($user)
->get(route('profile.edit'))
->assertInertia(fn (Assert $page) => $page
->where('user.id', $user->id)
->where('user.email', 'john@example.com')
->where('user.username', 'johndoe')
->where('user.userProfile.full_name', 'John Doe')
->where('user.userProfile.phone_number', '08123456789')
->where('user.userProfile.gender', 'male')
->where('user.userProfile.birth_date', '1990-05-15')
->where('user.userProfile.address', 'Jl. Sudirman No. 1')
);
});
test('profile page works when user has no profile', function () {
$user = User::factory()->create();
$this->actingAs($user)
->get(route('profile.edit'))
->assertInertia(fn (Assert $page) => $page
->where('user.id', $user->id)
->where('user.email', $user->email)
->where('user.username', $user->username)
->where('user.userProfile', null)
);
});
test('profile page passes mustVerifyEmail flag', function () {
$user = User::factory()->create();
$this->actingAs($user)
->get(route('profile.edit'))
->assertInertia(fn (Assert $page) => $page
->has('mustVerifyEmail')
);
});
/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - SUCCESS
|--------------------------------------------------------------------------
*/
test('profile information can be updated with all fields', function () {
$user = User::factory()->create(['username' => 'originaluser']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'updateduser',
'full_name' => 'Updated Name',
'phone_number' => '08987654321',
'gender' => 'female',
'birth_date' => '1995-03-20',
'address' => 'Jl. Thamrin No. 2',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('profile.edit'));
$user->refresh();
expect($user->username)->toBe('updateduser');
expect($user->userProfile->full_name)->toBe('Updated Name');
expect($user->userProfile->phone_number)->toBe('08987654321');
expect($user->userProfile->gender->value)->toBe('female');
expect($user->userProfile->birth_date->format('Y-m-d'))->toBe('1995-03-20');
expect($user->userProfile->address)->toBe('Jl. Thamrin No. 2');
});
test('profile can be updated with only required fields', function () {
$user = User::factory()->create(['username' => 'minimaluser']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'minimaluser',
'full_name' => 'Minimal Update',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('profile.edit'));
expect($user->refresh()->userProfile->full_name)->toBe('Minimal Update');
});
test('profile update creates user profile if not exists', function () {
$user = User::factory()->create(['username' => 'newprofile']);
expect($user->userProfile)->toBeNull();
$this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'newprofile',
'full_name' => 'New Profile',
'phone_number' => '08111111111',
'gender' => 'male',
'birth_date' => '2000-01-01',
'address' => 'New Address',
]);
$user->refresh();
expect($user->userProfile)->not->toBeNull();
expect($user->userProfile->full_name)->toBe('New Profile');
expect($user->userProfile->phone_number)->toBe('08111111111');
});
test('profile update updates existing user profile', function () {
$user = User::factory()->create(['username' => 'existingprofile']);
$user->userProfile()->create([
'full_name' => 'Old Name',
'phone_number' => '08000000000',
'gender' => 'male',
'address' => 'Old Address',
]);
$this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'existingprofile',
'full_name' => 'New Name',
'phone_number' => '08999999999',
'gender' => 'female',
'address' => 'New Address',
]);
$user->refresh();
expect($user->userProfile->full_name)->toBe('New Name');
expect($user->userProfile->phone_number)->toBe('08999999999');
expect($user->userProfile->gender->value)->toBe('female');
expect($user->userProfile->address)->toBe('New Address');
$this->assertDatabaseCount('user_profiles', 1);
});
test('profile update with null optional fields saves nulls', function () {
$user = User::factory()->create(['username' => 'nulltest']);
$user->userProfile()->create([
'full_name' => 'Has Data',
'phone_number' => '08123456789',
'gender' => 'male',
'birth_date' => '1990-01-01',
'address' => 'Some Address',
]);
$this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'nulltest',
'full_name' => 'Still Has Name',
'phone_number' => null,
'gender' => null,
'birth_date' => null,
'address' => null,
]);
$user->refresh();
expect($user->userProfile->full_name)->toBe('Still Has Name');
expect($user->userProfile->phone_number)->toBeNull();
expect($user->userProfile->gender)->toBeNull();
expect($user->userProfile->birth_date)->toBeNull();
expect($user->userProfile->address)->toBeNull();
});
/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: EMAIL
|--------------------------------------------------------------------------
*/
test('email is required', function () {
$user = User::factory()->create(['username' => 'emailreq']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'username' => 'emailreq',
'full_name' => 'Test',
]);
$response->assertSessionHasErrors('email');
});
test('email must be valid format', function () {
$user = User::factory()->create(['username' => 'emailfmt']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => 'not-an-email',
'username' => 'emailfmt',
'full_name' => 'Test',
]);
$response->assertSessionHasErrors('email');
});
test('email must not exceed 255 characters', function () {
$user = User::factory()->create(['username' => 'emailmax']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => str_repeat('a', 246).'@example.com',
'username' => 'emailmax',
'full_name' => 'Test',
]);
$response->assertSessionHasErrors('email');
});
test('email must be unique excluding self', function () {
$user = User::factory()->create(['email' => 'user1@example.com', 'username' => 'uniqueemail1']);
User::factory()->create(['email' => 'user2@example.com', 'username' => 'uniqueemail2']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => 'user2@example.com',
'username' => 'uniqueemail1',
'full_name' => 'Test',
]);
$response->assertSessionHasErrors('email');
});
test('user can keep their own email on update', function () {
$user = User::factory()->create(['email' => 'keep@example.com', 'username' => 'keepemail']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => 'keep@example.com',
'username' => 'keepemail',
'full_name' => 'Test',
]);
$response->assertSessionHasNoErrors();
});
/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: USERNAME
|--------------------------------------------------------------------------
*/
test('username is required', function () {
$user = User::factory()->create(['username' => 'usernamereq']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'full_name' => 'Test',
]);
$response->assertSessionHasErrors('username');
});
test('username must not exceed 20 characters', function () {
$user = User::factory()->create(['username' => 'usernamemax']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => str_repeat('a', 21),
'full_name' => 'Test',
]);
$response->assertSessionHasErrors('username');
});
test('username must be alpha_dash', function () {
$user = User::factory()->create(['username' => 'alphauser']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'has spaces!',
'full_name' => 'Test',
]);
$response->assertSessionHasErrors('username');
});
test('username allows dashes and underscores', function () {
$user = User::factory()->create(['username' => 'dashuser']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'user-name_123',
'full_name' => 'Test',
]);
$response->assertSessionHasNoErrors();
});
test('username must be unique excluding self', function () {
$user = User::factory()->create(['username' => 'uniqueuser1']);
User::factory()->create(['username' => 'uniqueuser2']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'uniqueuser2',
'full_name' => 'Test',
]);
$response->assertSessionHasErrors('username');
});
test('user can keep their own username on update', function () {
$user = User::factory()->create(['username' => 'keepuser']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'keepuser',
'full_name' => 'Test',
]);
$response->assertSessionHasNoErrors();
});
/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: FULL NAME
|--------------------------------------------------------------------------
*/
test('full_name is required', function () {
$user = User::factory()->create(['username' => 'fullnamereq']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'fullnamereq',
]);
$response->assertSessionHasErrors('full_name');
});
test('full_name must not exceed 200 characters', function () {
$user = User::factory()->create(['username' => 'fullnamemax']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'fullnamemax',
'full_name' => str_repeat('a', 201),
]);
$response->assertSessionHasErrors('full_name');
});
/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: OPTIONAL FIELDS
|--------------------------------------------------------------------------
*/
test('phone_number is optional', function () {
$user = User::factory()->create(['username' => 'phonetest']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'phonetest',
'full_name' => 'Test',
'phone_number' => null,
]);
$response->assertSessionHasNoErrors();
});
test('phone_number must not exceed 20 characters', function () {
$user = User::factory()->create(['username' => 'phonemax']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'phonemax',
'full_name' => 'Test',
'phone_number' => str_repeat('1', 21),
]);
$response->assertSessionHasErrors('phone_number');
});
test('gender must be valid enum value', function () {
$user = User::factory()->create(['username' => 'genderbad']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'genderbad',
'full_name' => 'Test',
'gender' => 'other',
]);
$response->assertSessionHasErrors('gender');
});
test('gender accepts male', function () {
$user = User::factory()->create(['username' => 'gendermale']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'gendermale',
'full_name' => 'Test',
'gender' => 'male',
]);
$response->assertSessionHasNoErrors();
});
test('gender accepts female', function () {
$user = User::factory()->create(['username' => 'genderfemale']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'genderfemale',
'full_name' => 'Test',
'gender' => 'female',
]);
$response->assertSessionHasNoErrors();
});
test('birth_date is optional', function () {
$user = User::factory()->create(['username' => 'birthnull']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'birthnull',
'full_name' => 'Test',
'birth_date' => null,
]);
$response->assertSessionHasNoErrors();
});
test('birth_date must be valid date', function () {
$user = User::factory()->create(['username' => 'birthbad']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'birthbad',
'full_name' => 'Test',
'birth_date' => 'not-a-date',
]);
$response->assertSessionHasErrors('birth_date');
});
test('address is optional', function () {
$user = User::factory()->create(['username' => 'addrnull']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'addrnull',
'full_name' => 'Test',
'address' => null,
]);
$response->assertSessionHasNoErrors();
});
/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: EMPTY REQUEST
|--------------------------------------------------------------------------
*/
test('user cannot update profile without submitting any data', function () {
$user = User::factory()->create(['username' => 'emptyreq']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), []);
$response->assertSessionHasErrors(['email', 'username', 'full_name']);
});
/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - TOAST / FLASH
|--------------------------------------------------------------------------
*/
test('profile update flashes success toast via inertia', function () {
$user = User::factory()->create(['username' => 'toastuser']);
$response = $this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'toastuser',
'full_name' => 'Toast Test',
]);
$response->assertRedirect();
});
/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - DATA INTEGRITY
|--------------------------------------------------------------------------
*/
test('profile update preserves other user fields', function () {
$user = User::factory()->create(['username' => 'preservetest']);
$this
->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'preservetest',
'full_name' => 'New Name',
]);
$user->refresh();
expect(Hash::check('password', $user->password))->toBeTrue();
});
test('profile update with realistic indonesian data', function () {
$user = User::factory()->create(['email' => 'budi.santoso@gmail.com', 'username' => 'budisan']);
$this
->actingAs($user)
->patch(route('profile.update'), [
'email' => 'budi.santoso@gmail.com',
'username' => 'budisan',
'full_name' => 'Budi Santoso',
'phone_number' => '08123456789',
'gender' => 'male',
'birth_date' => '1990-05-15',
'address' => 'Jl. Sudirman No. 123, Jakarta Selatan',
]);
$user->refresh();
expect($user->email)->toBe('budi.santoso@gmail.com');
expect($user->username)->toBe('budisan');
expect($user->userProfile->full_name)->toBe('Budi Santoso');
expect($user->userProfile->phone_number)->toBe('08123456789');
expect($user->userProfile->gender->value)->toBe('male');
expect($user->userProfile->birth_date->format('Y-m-d'))->toBe('1990-05-15');
expect($user->userProfile->address)->toBe('Jl. Sudirman No. 123, Jakarta Selatan');
});
/*
|--------------------------------------------------------------------------
| ROUTE TESTS
|--------------------------------------------------------------------------
*/
test('settings redirect goes to profile edit', function () {
$user = User::factory()->create(['username' => 'redirtest']);
$response = $this
->actingAs($user)
->get('/settings');
$response->assertRedirect('/settings/profile');
});
/*
|--------------------------------------------------------------------------
| REALISTIC SCENARIOS
|--------------------------------------------------------------------------
*/
test('user updates profile then views it again', function () {
$user = User::factory()->create(['username' => 'scenariouser']);
$this->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'scenariouser',
'full_name' => 'Scenario User',
'phone_number' => '08123456789',
'gender' => 'female',
'birth_date' => '1995-08-20',
'address' => 'Jl. Gatot Subroto No. 45',
]);
$this->actingAs($user->fresh())
->get(route('profile.edit'))
->assertInertia(fn (Assert $page) => $page
->where('user.userProfile.full_name', 'Scenario User')
->where('user.userProfile.phone_number', '08123456789')
->where('user.userProfile.gender', 'female')
->where('user.userProfile.birth_date', '1995-08-20')
->where('user.userProfile.address', 'Jl. Gatot Subroto No. 45')
);
});
test('user updates profile multiple times keeps only latest data', function () {
$user = User::factory()->create(['username' => 'multitimes']);
$this->actingAs($user)
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'multitimes',
'full_name' => 'First Update',
'phone_number' => '08111111111',
]);
$this->actingAs($user->fresh())
->patch(route('profile.update'), [
'email' => $user->email,
'username' => 'multitimes',
'full_name' => 'Second Update',
'phone_number' => '08222222222',
]);
$user->refresh();
expect($user->userProfile->full_name)->toBe('Second Update');
expect($user->userProfile->phone_number)->toBe('08222222222');
$this->assertDatabaseCount('user_profiles', 1);
});