406 lines
12 KiB
PHP
406 lines
12 KiB
PHP
<?php
|
|
|
|
use App\Enums\UserStatus;
|
|
use App\Livewire\Studio\Master\User\Index;
|
|
use App\Models\Employee;
|
|
use App\Models\Outlet;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Livewire\Livewire;
|
|
use Spatie\Permission\Models\Role;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
$roles = collect(['Developer', 'Owner', 'Leader', 'Admin', 'Partner', 'Customer'])
|
|
->map(fn ($role) => Role::create(['name' => $role]));
|
|
|
|
$this->user = User::factory()
|
|
->active()
|
|
->has(Employee::factory())
|
|
->create();
|
|
|
|
$this->user->roles()->attach($roles->pluck('id'));
|
|
});
|
|
|
|
function createIndexTestUser(array $userAttributes = [], array $employeeAttributes = [])
|
|
{
|
|
$factory = User::factory();
|
|
|
|
if (! isset($userAttributes['status'])) {
|
|
$factory = $factory->active();
|
|
unset($userAttributes['status']);
|
|
}
|
|
|
|
if (! empty($userAttributes)) {
|
|
$factory = $factory->state($userAttributes);
|
|
}
|
|
|
|
return $factory
|
|
->has(Employee::factory()->state($employeeAttributes))
|
|
->create();
|
|
}
|
|
|
|
function mountIndexComponent(User $user)
|
|
{
|
|
return Livewire::actingAs($user)->test(Index::class);
|
|
}
|
|
|
|
it('renders page successfully', function () {
|
|
mountIndexComponent($this->user)
|
|
->assertViewIs('livewire.studio.master.user.index')
|
|
->assertViewHas('pageTitle', 'Pegawai');
|
|
});
|
|
|
|
it('mounts employees correctly', function () {
|
|
$employee = createIndexTestUser();
|
|
$employee->assignRole('Admin');
|
|
|
|
mountIndexComponent($this->user)
|
|
->assertViewHas('employees', function ($employees) use ($employee) {
|
|
return collect($employees)->contains(
|
|
fn ($e) => $e['full_name'] === $employee->employee->full_name &&
|
|
$e['code'] === $employee->employee->code
|
|
);
|
|
});
|
|
});
|
|
|
|
it('loads employee data with all required fields', function () {
|
|
$outlet = Outlet::factory()->create();
|
|
$user = createIndexTestUser();
|
|
$user->assignRole('Admin');
|
|
$user->outlets()->attach($outlet->id);
|
|
$user->referralCode()->create(['code' => 'REF123']);
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
$employeeData = collect($component->get('employees'))->first(
|
|
fn ($e) => $e['full_name'] === $user->employee->full_name
|
|
);
|
|
|
|
expect($employeeData)->toHaveKeys([
|
|
'full_name',
|
|
'code',
|
|
'phone_number',
|
|
'address',
|
|
'base_salary',
|
|
'gender',
|
|
'birthdate',
|
|
'birth_age',
|
|
'hire_date',
|
|
'hire_ago',
|
|
'resign_date',
|
|
'resign_ago',
|
|
'user',
|
|
'referral_code',
|
|
'outlets',
|
|
'roles',
|
|
]);
|
|
|
|
expect($employeeData['user'])->toHaveKeys(['id', 'hash', 'email', 'username', 'status']);
|
|
expect($employeeData['gender'])->toHaveKeys(['label', 'color']);
|
|
expect($employeeData['user']['status'])->toHaveKeys(['gradient']);
|
|
});
|
|
|
|
it('excludes developer users from listing', function () {
|
|
$developer = createIndexTestUser();
|
|
$developer->assignRole('Developer');
|
|
|
|
$admin = createIndexTestUser();
|
|
$admin->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$employeeNames = collect($component->get('employees'))->pluck('full_name')->toArray();
|
|
|
|
expect($employeeNames)->not->toContain($developer->employee->full_name);
|
|
expect($employeeNames)->toContain($admin->employee->full_name);
|
|
});
|
|
|
|
it('displays all employees', function () {
|
|
$employees = [];
|
|
|
|
for ($i = 0; $i < 5; $i++) {
|
|
$user = createIndexTestUser();
|
|
$user->assignRole('Admin');
|
|
$employees[] = $user;
|
|
}
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
$employeeCount = count($component->get('employees'));
|
|
|
|
expect($employeeCount)->toBeGreaterThanOrEqual(5);
|
|
});
|
|
|
|
it('filters employees by search query - full name', function () {
|
|
$user1 = createIndexTestUser([], ['full_name' => 'John Doe']);
|
|
$user1->assignRole('Admin');
|
|
|
|
$user2 = createIndexTestUser([], ['full_name' => 'Jane Smith']);
|
|
$user2->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
expect(count($component->get('employees')))->toBeGreaterThanOrEqual(2);
|
|
|
|
$component->set('search', 'John')
|
|
->assertSet('search', 'John');
|
|
|
|
$employees = $component->get('employees');
|
|
expect(count($employees))->toBeGreaterThanOrEqual(1);
|
|
expect($employees[0]['full_name'])->toBe('John Doe');
|
|
});
|
|
|
|
it('filters employees by search query - employee code', function () {
|
|
$user1 = createIndexTestUser([], ['code' => 'EMP20240001']);
|
|
$user1->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$component->set('search', 'EMP20240001');
|
|
|
|
$employees = $component->get('employees');
|
|
expect(collect($employees)->pluck('code'))->toContain('EMP20240001');
|
|
});
|
|
|
|
it('filters employees by search query - email', function () {
|
|
$user1 = createIndexTestUser(['email' => 'john@example.com']);
|
|
$user1->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$component->set('search', 'john@example.com');
|
|
|
|
$employees = $component->get('employees');
|
|
expect(collect($employees)->pluck('user.email'))->toContain('john@example.com');
|
|
});
|
|
|
|
it('filters employees by search query - username', function () {
|
|
$user1 = createIndexTestUser(['username' => 'johndoe']);
|
|
$user1->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$component->set('search', 'johndoe');
|
|
|
|
$employees = $component->get('employees');
|
|
expect(collect($employees)->pluck('user.username'))->toContain('johndoe');
|
|
});
|
|
|
|
it('filters employees by status - active', function () {
|
|
$activeUser = createIndexTestUser(['status' => UserStatus::ACTIVE]);
|
|
$activeUser->assignRole('Admin');
|
|
|
|
$inactiveUser = createIndexTestUser(['status' => UserStatus::INACTIVE]);
|
|
$inactiveUser->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
expect(count($component->get('employees')))->toBeGreaterThanOrEqual(2);
|
|
|
|
$component->set('status', [UserStatus::ACTIVE->value]);
|
|
|
|
$employees = $component->get('employees');
|
|
$activeEmployees = collect($employees)->filter(
|
|
fn ($e) => $e['user']['status']['gradient'] === (UserStatus::ACTIVE->gradient())
|
|
);
|
|
|
|
expect(count($activeEmployees))->toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
it('filters employees by status - inactive', function () {
|
|
$activeUser = createIndexTestUser(['status' => UserStatus::ACTIVE]);
|
|
$activeUser->assignRole('Admin');
|
|
|
|
$inactiveUser = createIndexTestUser(['status' => UserStatus::INACTIVE]);
|
|
$inactiveUser->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$component->set('status', [UserStatus::INACTIVE->value]);
|
|
|
|
$employees = $component->get('employees');
|
|
$inactiveEmployees = collect($employees)->filter(
|
|
fn ($e) => $e['user']['status']['gradient'] === (UserStatus::INACTIVE->gradient())
|
|
);
|
|
|
|
expect(count($inactiveEmployees))->toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
it('filters employees by multiple statuses', function () {
|
|
$activeUser = createIndexTestUser(['status' => UserStatus::ACTIVE]);
|
|
$activeUser->assignRole('Admin');
|
|
|
|
$inactiveUser = createIndexTestUser(['status' => UserStatus::INACTIVE]);
|
|
$inactiveUser->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$component->set('status', [
|
|
UserStatus::ACTIVE->value,
|
|
UserStatus::INACTIVE->value,
|
|
]);
|
|
|
|
expect(count($component->get('employees')))->toBeGreaterThanOrEqual(2);
|
|
});
|
|
|
|
it('filters employees by both search and status', function () {
|
|
$activeUser = createIndexTestUser(
|
|
['status' => UserStatus::ACTIVE],
|
|
['full_name' => 'Active User']
|
|
);
|
|
$activeUser->assignRole('Admin');
|
|
|
|
$inactiveUser = createIndexTestUser(
|
|
['status' => UserStatus::INACTIVE],
|
|
['full_name' => 'Inactive User']
|
|
);
|
|
$inactiveUser->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user)
|
|
->set('search', 'Active')
|
|
->set('status', [UserStatus::ACTIVE->value]);
|
|
|
|
$employees = $component->get('employees');
|
|
expect(count($employees))->toBeGreaterThanOrEqual(1);
|
|
expect($employees[0]['full_name'])->toBe('Active User');
|
|
});
|
|
|
|
it('loads employees ordered by latest', function () {
|
|
$user1 = createIndexTestUser([], ['full_name' => 'First Employee']);
|
|
$user1->assignRole('Admin');
|
|
|
|
sleep(1);
|
|
|
|
$user2 = createIndexTestUser([], ['full_name' => 'Second Employee']);
|
|
$user2->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$employees = $component->get('employees');
|
|
$firstEmployee = collect($employees)->first(fn ($e) => $e['full_name'] === 'Second Employee');
|
|
$secondEmployee = collect($employees)->first(fn ($e) => $e['full_name'] === 'First Employee');
|
|
|
|
expect($firstEmployee)->not->toBeNull();
|
|
expect($secondEmployee)->not->toBeNull();
|
|
|
|
// The second employee should appear before the first
|
|
$firstIndex = array_search('Second Employee', array_column($employees, 'full_name'));
|
|
$secondIndex = array_search('First Employee', array_column($employees, 'full_name'));
|
|
|
|
expect($firstIndex)->toBeLessThan($secondIndex);
|
|
});
|
|
|
|
it('loads employee with outlets', function () {
|
|
$outlet1 = Outlet::factory()->create(['name' => 'Outlet A']);
|
|
$outlet2 = Outlet::factory()->create(['name' => 'Outlet B']);
|
|
|
|
$user = createIndexTestUser();
|
|
$user->assignRole('Admin');
|
|
$user->outlets()->attach([$outlet1->id, $outlet2->id]);
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$employeeData = collect($component->get('employees'))->first(
|
|
fn ($e) => $e['full_name'] === $user->employee->full_name
|
|
);
|
|
|
|
expect($employeeData['outlets'])->toContain('Outlet A', 'Outlet B');
|
|
});
|
|
|
|
it('loads employee with roles', function () {
|
|
$user = createIndexTestUser();
|
|
$user->assignRole(['Admin', 'Leader']);
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$employeeData = collect($component->get('employees'))->first(
|
|
fn ($e) => $e['full_name'] === $user->employee->full_name
|
|
);
|
|
|
|
expect($employeeData['roles'])->toContain('Admin', 'Leader');
|
|
});
|
|
|
|
it('loads employee with referral code', function () {
|
|
$user = createIndexTestUser();
|
|
$user->assignRole('Admin');
|
|
$user->referralCode()->create(['code' => 'REF12345']);
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$employeeData = collect($component->get('employees'))->first(
|
|
fn ($e) => $e['full_name'] === $user->employee->full_name
|
|
);
|
|
|
|
expect($employeeData['referral_code'])->toBe('REF12345');
|
|
});
|
|
|
|
it('deletes an employee successfully', function () {
|
|
$employeeUser = createIndexTestUser();
|
|
$employeeUser->assignRole('Admin');
|
|
|
|
mountIndexComponent($this->user)
|
|
->call('delete', $employeeUser)
|
|
->assertHasNoErrors();
|
|
|
|
$this->assertSoftDeleted('users', ['id' => $employeeUser->id]);
|
|
$this->assertSoftDeleted('employees', ['user_id' => $employeeUser->id]);
|
|
});
|
|
|
|
it('reloads employees after deletion', function () {
|
|
$employeeUser = createIndexTestUser();
|
|
$employeeUser->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
$initialCount = count($component->get('employees'));
|
|
|
|
$component->call('delete', $employeeUser);
|
|
|
|
expect(count($component->get('employees')))->toBeLessThan($initialCount);
|
|
});
|
|
|
|
it('resets password successfully', function () {
|
|
$defaultPassword = config('myconfig.password_default');
|
|
$employeeUser = createIndexTestUser();
|
|
$employeeUser->assignRole('Admin');
|
|
$employeeUser->update(['password' => Hash::make('oldpassword')]);
|
|
|
|
$oldPasswordHash = $employeeUser->password;
|
|
|
|
mountIndexComponent($this->user)
|
|
->call('resetPassword', $employeeUser)
|
|
->assertHasNoErrors();
|
|
|
|
$employeeUser->refresh();
|
|
|
|
expect($employeeUser->password)->not->toBe($oldPasswordHash);
|
|
expect(Hash::check($defaultPassword, $employeeUser->password))->toBeTrue();
|
|
});
|
|
|
|
it('does not display delete and edit buttons for current authenticated user', function () {
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$component
|
|
->assertDontSee('Delete')
|
|
->assertDontSee('Edit');
|
|
});
|
|
|
|
it('updates search with debounce', function () {
|
|
$user1 = createIndexTestUser([], ['full_name' => 'John Doe']);
|
|
$user1->assignRole('Admin');
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$component->set('search', 'John');
|
|
|
|
expect($component->get('search'))->toBe('John');
|
|
});
|
|
|
|
it('updates status filter correctly', function () {
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
$component->set('status', ['active']);
|
|
|
|
expect($component->get('status'))->toBeArray();
|
|
});
|