user = User::factory() ->active() ->has(Employee::factory()) ->create(); }); function createEditUser(array $attributes = []): User { return User::factory() ->active() ->has(Employee::factory()) ->state(fn () => array_merge([ 'status' => fake()->randomElement(UserStatus::cases()), ], $attributes)) ->create(); } function mountEditComponent(User $user, User $employeeUser): Testable { return Livewire::actingAs($user)->test(Edit::class, ['user' => $employeeUser]); } it('renders page successfully', function () { $employeeUser = createEditUser(); \Spatie\Permission\Models\Role::firstOrCreate(['name' => 'Admin']); $employeeUser->assignRole('Admin'); mountEditComponent($this->user, $employeeUser) ->assertViewIs('livewire.studio.master.user.form') ->assertViewHas('pageTitle', 'Ubah Pegawai'); }); it('loads outlets and roles on mount', function () { $employeeUser = createEditUser(); $outlet = Outlet::factory()->create(); $role = Role::create(['name' => 'Admin']); $component = mountEditComponent($this->user, $employeeUser); expect($component->outlets)->toBeArray(); expect($component->outlets)->toHaveKey($outlet->id); expect($component->roles)->toBeArray(); expect($component->roles)->toHaveKey($role->id); }); it('loads existing user data into form', function () { $outlet = Outlet::factory()->create(); $role = Role::create(['name' => 'Admin']); $employeeUser = createEditUser([ 'username' => 'existinguser', 'email' => 'existing@example.com', 'status' => UserStatus::ACTIVE, ]); $employeeUser->employee->update([ 'full_name' => 'Existing User', 'phone_number' => '081234567890', 'base_salary' => 5000000, 'birthdate' => '1995-01-15', 'hire_date' => '2020-01-01', 'address' => 'Existing Address', 'gender' => Gender::MALE, ]); $employeeUser->assignRole($role->id); $employeeUser->outlets()->attach($outlet->id); $component = mountEditComponent($this->user, $employeeUser); expect($component->form->full_name)->toBe('Existing User') ->and($component->form->username)->toBe('existinguser') ->and($component->form->email)->toBe('existing@example.com') ->and($component->form->phone_number)->toBe('081234567890') ->and($component->form->base_salary)->toBe('5000000'); expect($component->form->birthdate)->toBe('1995-01-15'); expect($component->form->hire_date)->toBe('2020-01-01'); expect($component->form->address)->toBe('Existing Address'); expect((int) $component->form->gender)->toBe(Gender::MALE->value); expect((int) $component->form->status)->toBe(UserStatus::ACTIVE->value); expect($component->form->outlet_ids)->toContain($outlet->id); expect($component->form->role_ids)->toContain($role->id); }); it('excludes developer role from roles list', function () { Role::firstOrCreate(['name' => 'Admin']); Role::firstOrCreate(['name' => 'Developer']); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->assignRole($role->id); $component = mountEditComponent($this->user, $employeeUser); $developerRole = Role::where('name', 'Developer')->first(); expect($component->roles)->not->toHaveKey($developerRole->id); }); it('prevents update when user is unauthorized', function () { Role::firstOrCreate(['name' => 'Admin']); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->assignRole($role->id); Gate::define('update user', fn () => false); mountEditComponent($this->user, $employeeUser) ->call('save') ->assertForbidden(); }); it('updates user successfully when authorized', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $outlet = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->assignRole($role->id); $employeeUser->outlets()->attach($outlet->id); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.full_name', 'Updated Name') ->set('form.username', 'updateduser') ->set('form.email', 'updated@example.com') ->set('form.phone_number', '0813 9999 8888') ->set('form.base_salary', '6.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.address', 'Updated Address') ->set('form.gender', Gender::FEMALE->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors() ->assertRedirect(Index::class); $employeeUser->refresh(); $employeeUser->load(['employee', 'outlets', 'roles']); expect($employeeUser->username)->toBe('updateduser'); expect($employeeUser->email)->toBe('updated@example.com'); expect($employeeUser->status)->toBe(UserStatus::ACTIVE); expect($employeeUser->employee->full_name)->toBe('Updated Name'); expect($employeeUser->employee->phone_number)->toBe('0813 9999 8888'); expect($employeeUser->employee->base_salary)->toBe(6000000); expect($employeeUser->employee->address)->toBe('Updated Address'); expect($employeeUser->employee->gender)->toBe(Gender::FEMALE); expect($employeeUser->outlets->pluck('id')->toArray())->toContain($outlet->id); expect($employeeUser->roles->pluck('id')->toArray())->toContain($role->id); }); it('updates user with inactive status and sets resign date', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $outlet = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(['status' => UserStatus::ACTIVE]); $employeeUser->assignRole($role->id); $employeeUser->outlets()->attach($outlet->id); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.status', UserStatus::INACTIVE->value) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors(); $employeeUser->refresh(); $employeeUser->load('employee'); expect($employeeUser->status)->toBe(UserStatus::INACTIVE); expect($employeeUser->employee->resign_date)->not->toBeNull(); }); it('clears resign date when status changes to active', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $outlet = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser([ 'status' => UserStatus::INACTIVE, ]); $employeeUser->assignRole($role->id); $employeeUser->outlets()->attach($outlet->id); $employee = $employeeUser->employee; $employee->update(['resign_date' => now()->subDays(5)]); expect($employee->resign_date)->not->toBeNull(); $birthdate = $employee->birthdate; $hireDate = $employee->hire_date; mountEditComponent($this->user, $employeeUser) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.full_name', $employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employee->gender->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors(); $employeeUser->refresh(); expect($employeeUser->status)->toBe(UserStatus::ACTIVE); expect($employee->resign_date)->not->toBeNull(); }); it('updates user with multiple outlets', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $outlet1 = Outlet::factory()->create(); $outlet2 = Outlet::factory()->create(); $outlet3 = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->assignRole($role->id); $employeeUser->outlets()->attach($outlet1->id); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet1->id, $outlet2->id, $outlet3->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors(); $employeeUser->refresh(); $employeeUser->load('outlets'); expect($employeeUser->outlets->count())->toBe(3); expect($employeeUser->outlets->pluck('id')->toArray())->toContain($outlet1->id, $outlet2->id, $outlet3->id); }); it('updates user with multiple roles', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $outlet = Outlet::factory()->create(); $adminRole = Role::firstOrCreate(['name' => 'Admin']); $leaderRole = Role::firstOrCreate(['name' => 'Leader']); $employeeUser = createEditUser(); $employeeUser->assignRole($adminRole->id); $employeeUser->outlets()->attach($outlet->id); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$adminRole->id, $leaderRole->id]) ->call('save') ->assertHasNoErrors(); $employeeUser->refresh(); $employeeUser->load('roles'); expect($employeeUser->roles->count())->toBe(2); expect($employeeUser->roles->pluck('id')->toArray())->toContain($adminRole->id, $leaderRole->id); }); it('syncs outlets correctly when updating', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $outlet1 = Outlet::factory()->create(); $outlet2 = Outlet::factory()->create(); $outlet3 = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->assignRole($role->id); $employeeUser->outlets()->attach([$outlet1->id, $outlet2->id]); expect($employeeUser->outlets->count())->toBe(2); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet2->id, $outlet3->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors(); $employeeUser->refresh(); $employeeUser->load('outlets'); expect($employeeUser->outlets->count())->toBe(2); expect($employeeUser->outlets->pluck('id')->toArray())->toContain($outlet2->id, $outlet3->id); expect($employeeUser->outlets->pluck('id')->toArray())->not->toContain($outlet1->id); }); it('syncs roles correctly when updating', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $outlet = Outlet::factory()->create(); $adminRole = Role::firstOrCreate(['name' => 'Admin']); $leaderRole = Role::firstOrCreate(['name' => 'Leader']); $ownerRole = Role::firstOrCreate(['name' => 'Owner']); $employeeUser = createEditUser(); $employeeUser->assignRole([$adminRole->id, $leaderRole->id]); $employeeUser->outlets()->attach($outlet->id); expect($employeeUser->roles->count())->toBe(2); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$leaderRole->id, $ownerRole->id]) ->call('save') ->assertHasNoErrors(); $employeeUser->refresh(); $employeeUser->load('roles'); expect($employeeUser->roles->count())->toBe(2); expect($employeeUser->roles->pluck('id')->toArray())->toContain($leaderRole->id, $ownerRole->id); expect($employeeUser->roles->pluck('id')->toArray())->not->toContain($adminRole->id); }); it('validates required fields', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->assignRole($role->id); $component = mountEditComponent($this->user, $employeeUser); // Clear all fields $component->set('form.full_name', '') ->set('form.username', '') ->set('form.email', '') ->set('form.phone_number', '') ->set('form.base_salary', '') ->set('form.birthdate', '') ->set('form.hire_date', '') ->set('form.gender', '') ->set('form.outlet_ids', []) ->set('form.role_ids', []) ->call('save') ->assertHasErrors([ 'form.full_name', 'form.username', 'form.email', 'form.phone_number', 'form.base_salary', 'form.birthdate', 'form.hire_date', 'form.gender', 'form.outlet_ids', 'form.role_ids', ]); }); it('validates username uniqueness ignores current user', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(['username' => 'existinguser']); $employeeUser->assignRole($role->id); $outlet = Outlet::factory()->create(); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); // Should allow keeping the same username mountEditComponent($this->user, $employeeUser) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', 'existinguser') // Same username ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors(['form.username']); }); it('validates username uniqueness prevents duplicate', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $role = Role::firstOrCreate(['name' => 'Admin']); $existingUser = createEditUser(['username' => 'existinguser']); $existingUser->assignRole($role->id); $employeeUser = createEditUser(['username' => 'otheruser']); $employeeUser->assignRole($role->id); $outlet = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.username', 'existinguser') // Duplicate username ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasErrors(['form.username']); }); it('validates email uniqueness ignores current user', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(['email' => 'existing@example.com']); $employeeUser->assignRole($role->id); $outlet = Outlet::factory()->create(); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); // Should allow keeping the same email mountEditComponent($this->user, $employeeUser) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', 'existing@example.com') // Same email ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors(['form.email']); }); it('validates email uniqueness prevents duplicate', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $role = Role::firstOrCreate(['name' => 'Admin']); $existingUser = createEditUser(['email' => 'existing@example.com']); $existingUser->assignRole($role->id); $employeeUser = createEditUser(['email' => 'other@example.com']); $employeeUser->assignRole($role->id); $outlet = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.email', 'existing@example.com') // Duplicate email ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasErrors(['form.email']); }); it('validates birthdate must be before 18 years ago', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->assignRole($role->id); $outlet = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $birthdate = now()->subYears(17)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.birthdate', $birthdate) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasErrors(['form.birthdate']); }); it('formats base salary correctly when updating', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->assignRole($role->id); $outlet = Outlet::factory()->create(); $role = Role::firstOrCreate(['name' => 'Admin']); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.base_salary', '7.500.000') ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors(); $employeeUser->refresh(); $employeeUser->load('employee'); expect($employeeUser->employee->base_salary)->toBe(7500000); }); it('allows nullable address field when updating', function () { $permission = Permission::create(['name' => 'update user']); $this->user->givePermissionTo($permission); $role = Role::firstOrCreate(['name' => 'Admin']); $employeeUser = createEditUser(); $employeeUser->employee->update(['address' => 'Old Address']); $employeeUser->assignRole($role->id); $outlet = Outlet::factory()->create(); $birthdate = now()->subYears(25)->format('Y-m-d'); $hireDate = now()->subMonths(6)->format('Y-m-d'); mountEditComponent($this->user, $employeeUser) ->set('form.address', null) ->set('form.full_name', $employeeUser->employee->full_name) ->set('form.username', $employeeUser->username) ->set('form.email', $employeeUser->email) ->set('form.phone_number', $employeeUser->employee->phone_number) ->set('form.base_salary', '5.000.000') ->set('form.birthdate', $birthdate) ->set('form.hire_date', $hireDate) ->set('form.gender', $employeeUser->employee->gender->value) ->set('form.status', UserStatus::ACTIVE->value) ->set('form.outlet_ids', [$outlet->id]) ->set('form.role_ids', [$role->id]) ->call('save') ->assertHasNoErrors(); $employeeUser->refresh(); $employeeUser->load('employee'); expect($employeeUser->employee->address)->toBeNull(); });