seed(RolePermissionSeeder::class); $disk = config('filesystems.default') === 's3' ? 's3' : config('filesystems.default', 'public'); Storage::fake($disk); Storage::fake('public'); }); function createUserWithProfile(): User { $user = User::factory()->create(); $user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value); $user->forgetCachedPermissions(); UserProfile::factory()->create([ 'user_id' => $user->id, 'full_name' => fake()->name(), 'phone_number' => fake()->numerify('08##########'), 'gender' => fake()->randomElement(['male', 'female']), ]); return $user; } function createUserWithoutProfile(): User { $user = User::factory()->create(); $user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value); $user->forgetCachedPermissions(); return $user; } function validProfilePayload(): array { return [ 'email' => 'updated@example.com', 'username' => 'updated_user', 'full_name' => 'Nama Updated', 'phone_number' => '081234567890', 'gender' => 'male', 'birth_date' => '1995-01-15', 'address' => 'Jl. Merdeka No. 1', ]; } // ─── Profile Edit ────────────────────────────────────────── describe('Profile Edit', function () { test('authenticated user can view profile edit form', function () { $user = createUserWithProfile(); $this->actingAs($user) ->get(route('admin.account.profile')) ->assertOk(); }); test('guest is redirected to login', function () { $this->get(route('admin.account.profile')) ->assertRedirect(route('login')); }); }); // ─── Profile Update ──────────────────────────────────────── describe('Profile Update', function () { test('authenticated user can update profile', function () { $user = createUserWithProfile(); $this->actingAs($user) ->put(route('admin.account.profile.update'), validProfilePayload()) ->assertRedirect(route('admin.account.profile')); $user->refresh(); expect($user->email)->toBe('updated@example.com'); expect($user->username)->toBe('updated_user'); expect($user->profile->full_name)->toBe('Nama Updated'); expect($user->profile->phone_number)->toBe('081234567890'); expect($user->profile->gender->value)->toBe('male'); expect($user->profile->birth_date->format('Y-m-d'))->toBe('1995-01-15'); expect($user->profile->address)->toBe('Jl. Merdeka No. 1'); }); test('guest cannot update profile', function () { $this->put(route('admin.account.profile.update'), validProfilePayload()) ->assertRedirect(route('login')); }); test('profile is created when user has no profile', function () { $user = createUserWithoutProfile(); $this->actingAs($user) ->put(route('admin.account.profile.update'), validProfilePayload()) ->assertRedirect(route('admin.account.profile')); $user->refresh(); expect($user->profile)->not->toBeNull(); expect($user->profile->full_name)->toBe('Nama Updated'); }); test('required fields are validated', function () { $user = createUserWithProfile(); $this->actingAs($user) ->put(route('admin.account.profile.update'), []) ->assertSessionHasErrors(['email', 'username', 'full_name']); }); test('email must be unique on update (ignoring self)', function () { $user = createUserWithProfile(); $payload = validProfilePayload(); $payload['email'] = $user->email; $payload['username'] = $user->username; $this->actingAs($user) ->put(route('admin.account.profile.update'), $payload) ->assertSessionHasNoErrors('email'); }); test('email must be unique among other users', function () { $user = createUserWithProfile(); $other = User::factory()->create(['email' => 'taken@example.com']); $payload = validProfilePayload(); $payload['email'] = 'taken@example.com'; $this->actingAs($user) ->put(route('admin.account.profile.update'), $payload) ->assertSessionHasErrors('email'); }); test('username must be unique on update (ignoring self)', function () { $user = createUserWithProfile(); $payload = validProfilePayload(); $payload['email'] = $user->email; $payload['username'] = $user->username; $this->actingAs($user) ->put(route('admin.account.profile.update'), $payload) ->assertSessionHasNoErrors('username'); }); test('nullable fields can be null', function () { $user = createUserWithProfile(); $payload = validProfilePayload(); $payload['phone_number'] = null; $payload['gender'] = null; $payload['birth_date'] = null; $payload['address'] = null; $this->actingAs($user) ->put(route('admin.account.profile.update'), $payload) ->assertRedirect(route('admin.account.profile')); $user->refresh(); expect($user->profile->phone_number)->toBeNull(); expect($user->profile->gender)->toBeNull(); expect($user->profile->birth_date)->toBeNull(); expect($user->profile->address)->toBeNull(); }); test('updating profile with s3 key links the photo', function () { $user = createUserWithProfile(); $disk = config('filesystems.default') === 's3' ? 's3' : config('filesystems.default', 'public'); $imageContent = UploadedFile::fake()->image('avatar.jpg')->get(); Storage::disk($disk)->put('fake-s3-key.jpg', $imageContent); $payload = validProfilePayload(); $payload['profile_s3_key'] = 'fake-s3-key.jpg'; $this->actingAs($user) ->put(route('admin.account.profile.update'), $payload); $user->refresh(); expect($user->profile->getMedia('profile_photo')->count())->toBe(1); }); test('updating profile can upload new photo and delete existing photo', function () { $user = createUserWithProfile(); $imageContent = UploadedFile::fake()->image('initial.jpg')->get(); Storage::disk('public')->put('initial.jpg', $imageContent); $user->profile->addMediaFromDisk('initial.jpg', 'public')->toMediaCollection('profile_photo'); expect($user->profile->fresh()->getMedia('profile_photo')->count())->toBe(1); $mediaId = $user->profile->fresh()->getFirstMedia('profile_photo')->id; $disk = config('filesystems.default') === 's3' ? 's3' : config('filesystems.default', 'public'); $imageContent2 = UploadedFile::fake()->image('updated.jpg')->get(); Storage::disk($disk)->put('updated.jpg', $imageContent2); $payload = validProfilePayload(); $payload['profile_s3_key'] = 'updated.jpg'; $payload['remove_profile_photo_ids'] = [$mediaId]; $this->actingAs($user) ->put(route('admin.account.profile.update'), $payload); $freshProfile = $user->fresh()->profile; expect($freshProfile->getMedia('profile_photo')->count())->toBe(1); expect($freshProfile->getFirstMedia('profile_photo')->id)->not->toBe($mediaId); }); test('removing profile photo without uploading new one results in no photo', function () { $user = createUserWithProfile(); $imageContent = UploadedFile::fake()->image('initial.jpg')->get(); Storage::disk('public')->put('initial.jpg', $imageContent); $user->profile->addMediaFromDisk('initial.jpg', 'public')->toMediaCollection('profile_photo'); $mediaId = $user->profile->fresh()->getFirstMedia('profile_photo')->id; $payload = validProfilePayload(); $payload['remove_profile_photo_ids'] = [$mediaId]; $this->actingAs($user) ->put(route('admin.account.profile.update'), $payload); $user->refresh(); expect($user->profile->getMedia('profile_photo')->count())->toBe(0); }); }); // ─── Password Edit ───────────────────────────────────────── describe('Password Edit', function () { test('authenticated user can view password edit form', function () { $user = createUserWithProfile(); $this->actingAs($user) ->get(route('admin.account.password')) ->assertOk(); }); test('guest is redirected to login', function () { $this->get(route('admin.account.password')) ->assertRedirect(route('login')); }); }); // ─── Password Update ─────────────────────────────────────── describe('Password Update', function () { test('authenticated user can update password', function () { $user = User::factory()->create(['password' => Hash::make('old-password')]); $user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value); $user->forgetCachedPermissions(); $this->actingAs($user) ->put(route('admin.account.password.update'), [ 'current_password' => 'old-password', 'password' => 'new-secure-password', 'password_confirmation' => 'new-secure-password', ]) ->assertRedirect(route('admin.account.password')); $this->assertTrue(Hash::check('new-secure-password', $user->fresh()->password)); }); test('guest cannot update password', function () { $this->put(route('admin.account.password.update'), [ 'current_password' => 'old-password', 'password' => 'new-secure-password', 'password_confirmation' => 'new-secure-password', ])->assertRedirect(route('login')); }); test('current password must be correct', function () { $user = User::factory()->create(['password' => Hash::make('old-password')]); $user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value); $user->forgetCachedPermissions(); $this->actingAs($user) ->put(route('admin.account.password.update'), [ 'current_password' => 'wrong-password', 'password' => 'new-secure-password', 'password_confirmation' => 'new-secure-password', ]) ->assertSessionHasErrors('current_password'); }); test('password confirmation must match', function () { $user = User::factory()->create(['password' => Hash::make('old-password')]); $user->givePermissionTo(PermissionEnum::DASHBOARD_VIEW->value); $user->forgetCachedPermissions(); $this->actingAs($user) ->put(route('admin.account.password.update'), [ 'current_password' => 'old-password', 'password' => 'new-secure-password', 'password_confirmation' => 'different-password', ]) ->assertSessionHasErrors('password'); }); test('required fields are validated', function () { $user = createUserWithoutProfile(); $this->actingAs($user) ->put(route('admin.account.password.update'), []) ->assertSessionHasErrors(['current_password', 'password']); }); });