dstpabuaran.com/tests/Feature/Admin/HR/EmployeeTest.php

1033 lines
37 KiB
PHP

<?php
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Inertia\Testing\AssertableInertia as Assert;
uses(RefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| AUTHENTICATION
|--------------------------------------------------------------------------
*/
test('guests are redirected to the login page', function () {
$response = $this->get(route('admin.hr.employees.index'));
$response->assertRedirect(route('login'));
});
test('authenticated users can visit the employee index page', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->get(route('admin.hr.employees.index'));
$response->assertOk();
});
/*
|--------------------------------------------------------------------------
| INDEX PAGE
|--------------------------------------------------------------------------
*/
test('employee index page displays employees', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create();
$employeeUser->userProfile()->create([
'full_name' => 'Budi Santoso',
'phone_number' => '08123456789',
'gender' => 'male',
]);
$employeeUser->employee()->create([
'join_date' => '2024-01-15',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response = $this->get(route('admin.hr.employees.index'));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
);
});
test('index page works with zero employees', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->get(route('admin.hr.employees.index'));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 0)
);
});
test('index page does not display users without employee record', function () {
$user = User::factory()->create();
$this->actingAs($user);
User::factory()->create();
$response = $this->get(route('admin.hr.employees.index'));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 0)
);
});
test('index page displays correct count after delete', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create();
$employeeUser->userProfile()->create(['full_name' => 'Employee 1', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$employeeUser2 = User::factory()->create();
$employeeUser2->userProfile()->create(['full_name' => 'Employee 2', 'gender' => 'female']);
$employeeUser2->employee()->create(['join_date' => '2024-02-01', 'employment_status' => 'part_time', 'base_salary' => 3000000]);
$this->delete(route('admin.hr.employees.destroy', $employeeUser));
$response = $this->get(route('admin.hr.employees.index'));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
);
});
/*
|--------------------------------------------------------------------------
| FILTER - EMPLOYMENT STATUS
|--------------------------------------------------------------------------
*/
test('index can filter by employment status full_time', function () {
$user = User::factory()->create();
$this->actingAs($user);
$ft = User::factory()->create();
$ft->userProfile()->create(['full_name' => 'Full Time Employee', 'gender' => 'male']);
$ft->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$pt = User::factory()->create();
$pt->userProfile()->create(['full_name' => 'Part Time Employee', 'gender' => 'female']);
$pt->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'part_time', 'base_salary' => 3000000]);
$response = $this->get(route('admin.hr.employees.index', ['employment_status' => 'full_time']));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
->where('employees.0.user_profile.full_name', 'Full Time Employee')
);
});
test('index can filter by employment status contract', function () {
$user = User::factory()->create();
$this->actingAs($user);
$contract = User::factory()->create();
$contract->userProfile()->create(['full_name' => 'Contract Employee', 'gender' => 'male']);
$contract->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'contract', 'base_salary' => 4000000]);
$intern = User::factory()->create();
$intern->userProfile()->create(['full_name' => 'Intern Employee', 'gender' => 'female']);
$intern->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'internship', 'base_salary' => 2000000]);
$response = $this->get(route('admin.hr.employees.index', ['employment_status' => 'contract']));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
->where('employees.0.user_profile.full_name', 'Contract Employee')
);
});
test('index can filter by employment status resigned', function () {
$user = User::factory()->create();
$this->actingAs($user);
$active = User::factory()->create();
$active->userProfile()->create(['full_name' => 'Active Employee', 'gender' => 'male']);
$active->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$resigned = User::factory()->create();
$resigned->userProfile()->create(['full_name' => 'Resigned Employee', 'gender' => 'female']);
$resigned->employee()->create(['join_date' => '2024-01-01', 'resign_date' => '2024-06-01', 'employment_status' => 'resigned', 'base_salary' => 5000000]);
$response = $this->get(route('admin.hr.employees.index', ['employment_status' => 'resigned']));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
->where('employees.0.user_profile.full_name', 'Resigned Employee')
);
});
/*
|--------------------------------------------------------------------------
| FILTER - IS ACTIVE
|--------------------------------------------------------------------------
*/
test('index can filter by is_active true', function () {
$user = User::factory()->create();
$this->actingAs($user);
$active = User::factory()->create(['is_active' => true]);
$active->userProfile()->create(['full_name' => 'Active Employee', 'gender' => 'male']);
$active->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$inactive = User::factory()->create(['is_active' => false]);
$inactive->userProfile()->create(['full_name' => 'Inactive Employee', 'gender' => 'female']);
$inactive->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->get(route('admin.hr.employees.index', ['is_active' => '1']));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
->where('employees.0.user_profile.full_name', 'Active Employee')
);
});
test('index can filter by is_active false', function () {
$user = User::factory()->create();
$this->actingAs($user);
$active = User::factory()->create(['is_active' => true]);
$active->userProfile()->create(['full_name' => 'Active Employee', 'gender' => 'male']);
$active->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$inactive = User::factory()->create(['is_active' => false]);
$inactive->userProfile()->create(['full_name' => 'Inactive Employee', 'gender' => 'female']);
$inactive->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->get(route('admin.hr.employees.index', ['is_active' => '0']));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
->where('employees.0.user_profile.full_name', 'Inactive Employee')
);
});
/*
|--------------------------------------------------------------------------
| FILTER - GENDER
|--------------------------------------------------------------------------
*/
test('index can filter by gender male', function () {
$user = User::factory()->create();
$this->actingAs($user);
$male = User::factory()->create();
$male->userProfile()->create(['full_name' => 'Male Employee', 'gender' => 'male']);
$male->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$female = User::factory()->create();
$female->userProfile()->create(['full_name' => 'Female Employee', 'gender' => 'female']);
$female->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->get(route('admin.hr.employees.index', ['gender' => 'male']));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
->where('employees.0.user_profile.full_name', 'Male Employee')
);
});
test('index can filter by gender female', function () {
$user = User::factory()->create();
$this->actingAs($user);
$male = User::factory()->create();
$male->userProfile()->create(['full_name' => 'Male Employee', 'gender' => 'male']);
$male->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$female = User::factory()->create();
$female->userProfile()->create(['full_name' => 'Female Employee', 'gender' => 'female']);
$female->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->get(route('admin.hr.employees.index', ['gender' => 'female']));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
->where('employees.0.user_profile.full_name', 'Female Employee')
);
});
/*
|--------------------------------------------------------------------------
| FILTER - COMBINED
|--------------------------------------------------------------------------
*/
test('index can filter by multiple criteria combined', function () {
$user = User::factory()->create();
$this->actingAs($user);
$match = User::factory()->create(['is_active' => true]);
$match->userProfile()->create(['full_name' => 'Match Employee', 'gender' => 'male']);
$match->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$noMatch1 = User::factory()->create(['is_active' => true]);
$noMatch1->userProfile()->create(['full_name' => 'Female FT', 'gender' => 'female']);
$noMatch1->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$noMatch2 = User::factory()->create(['is_active' => false]);
$noMatch2->userProfile()->create(['full_name' => 'Male Inactive', 'gender' => 'male']);
$noMatch2->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$noMatch3 = User::factory()->create(['is_active' => true]);
$noMatch3->userProfile()->create(['full_name' => 'Male Contract', 'gender' => 'male']);
$noMatch3->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'contract', 'base_salary' => 4000000]);
$response = $this->get(route('admin.hr.employees.index', [
'employment_status' => 'full_time',
'is_active' => '1',
'gender' => 'male',
]));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
->where('employees.0.user_profile.full_name', 'Match Employee')
);
});
test('index passes filters to view', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->get(route('admin.hr.employees.index', [
'employment_status' => 'full_time',
'is_active' => '1',
'gender' => 'male',
]));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->where('filters.employment_status', 'full_time')
->where('filters.is_active', '1')
->where('filters.gender', 'male')
);
});
/*
|--------------------------------------------------------------------------
| CREATE / STORE
|--------------------------------------------------------------------------
*/
test('employee can be created', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'newemployee@example.com',
'username' => 'newemployee',
'full_name' => 'New Employee',
'phone_number' => '08123456789',
'gender' => 'male',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('admin.hr.employees.index'));
$this->assertDatabaseHas('users', [
'email' => 'newemployee@example.com',
'username' => 'newemployee',
]);
$this->assertDatabaseHas('user_profiles', [
'full_name' => 'New Employee',
'gender' => 'male',
]);
$this->assertDatabaseHas('employees', [
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
});
test('employee can be created without optional fields', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'minimal@example.com',
'username' => 'minimal',
'full_name' => 'Minimal Employee',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasNoErrors();
});
test('employee email is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'username' => 'noemail',
'full_name' => 'No Email',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('email');
});
test('employee email must be valid', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'not-an-email',
'username' => 'bademail',
'full_name' => 'Bad Email',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('email');
});
test('employee email must be unique', function () {
$user = User::factory()->create(['email' => 'existing@example.com']);
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'existing@example.com',
'username' => 'duplicate',
'full_name' => 'Duplicate Email',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('email');
});
test('employee username is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'nousername@example.com',
'full_name' => 'No Username',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('username');
});
test('employee username must be alpha_dash', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'badusername@example.com',
'username' => 'has spaces!',
'full_name' => 'Bad Username',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('username');
});
test('employee username must be unique', function () {
$user = User::factory()->create(['username' => 'existinguser']);
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'new@example.com',
'username' => 'existinguser',
'full_name' => 'Duplicate Username',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('username');
});
test('employee full_name is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'noname@example.com',
'username' => 'noname',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('full_name');
});
test('employee join_date is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'nojoindate@example.com',
'username' => 'nojoindate',
'full_name' => 'No Join Date',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('join_date');
});
test('employee join_date must be valid date', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'baddate@example.com',
'username' => 'baddate',
'full_name' => 'Bad Date',
'join_date' => 'not-a-date',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('join_date');
});
test('employee employment_status is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'nostatus@example.com',
'username' => 'nostatus',
'full_name' => 'No Status',
'join_date' => '2024-06-01',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('employment_status');
});
test('employee employment_status must be valid', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'badstatus@example.com',
'username' => 'badstatus',
'full_name' => 'Bad Status',
'join_date' => '2024-06-01',
'employment_status' => 'invalid_status',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('employment_status');
});
test('employee base_salary is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'nosalary@example.com',
'username' => 'nosalary',
'full_name' => 'No Salary',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
]);
$response->assertSessionHasErrors('base_salary');
});
test('employee base_salary must be integer', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'badsalary@example.com',
'username' => 'badsalary',
'full_name' => 'Bad Salary',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 'not-a-number',
]);
$response->assertSessionHasErrors('base_salary');
});
test('employee base_salary must not be negative', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'negsalary@example.com',
'username' => 'negsalary',
'full_name' => 'Negative Salary',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => -1000,
]);
$response->assertSessionHasErrors('base_salary');
});
test('employee gender must be valid enum', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'badgender@example.com',
'username' => 'badgender',
'full_name' => 'Bad Gender',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
'gender' => 'other',
]);
$response->assertSessionHasErrors('gender');
});
test('employee resign_date must be after or equal join_date', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'badresigndate@example.com',
'username' => 'badresigndate',
'full_name' => 'Bad Resign Date',
'join_date' => '2024-06-01',
'resign_date' => '2024-01-01',
'employment_status' => 'resigned',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('resign_date');
});
test('store flashes success toast via Inertia', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'toast@example.com',
'username' => 'toast',
'full_name' => 'Toast Employee',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertRedirect();
});
test('store creates user, profile, and employee in database', function () {
$user = User::factory()->create();
$this->actingAs($user);
$this->post(route('admin.hr.employees.store'), [
'email' => 'full@example.com',
'username' => 'fullcreate',
'full_name' => 'Full Create',
'phone_number' => '08123456789',
'gender' => 'female',
'join_date' => '2024-06-01',
'employment_status' => 'part_time',
'base_salary' => 3000000,
]);
$this->assertDatabaseCount('users', 2);
$this->assertDatabaseHas('users', ['email' => 'full@example.com']);
$this->assertDatabaseHas('user_profiles', ['full_name' => 'Full Create', 'gender' => 'female']);
$this->assertDatabaseHas('employees', ['employment_status' => 'part_time', 'base_salary' => 3000000]);
});
/*
|--------------------------------------------------------------------------
| EDIT / UPDATE
|--------------------------------------------------------------------------
*/
test('employee can be updated', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create(['username' => 'original_user']);
$employeeUser->userProfile()->create(['full_name' => 'Original Name', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->put(route('admin.hr.employees.update', $employeeUser), [
'email' => $employeeUser->email,
'username' => 'original_user',
'full_name' => 'Updated Name',
'phone_number' => '0987654321',
'gender' => 'female',
'join_date' => '2024-01-01',
'employment_status' => 'part_time',
'base_salary' => 3000000,
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('admin.hr.employees.index'));
$employeeUser->refresh();
expect($employeeUser->userProfile->full_name)->toBe('Updated Name');
expect($employeeUser->userProfile->gender->value)->toBe('female');
expect($employeeUser->employee->employment_status->value)->toBe('part_time');
expect($employeeUser->employee->base_salary)->toBe(3000000);
});
test('employee update email must be unique excluding itself', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create(['email' => 'first@example.com']);
$employeeUser->userProfile()->create(['full_name' => 'First', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$other = User::factory()->create(['email' => 'second@example.com']);
$response = $this->put(route('admin.hr.employees.update', $employeeUser), [
'email' => 'second@example.com',
'username' => $employeeUser->username,
'full_name' => 'First',
'join_date' => '2024-01-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('email');
});
test('employee update username must be unique excluding itself', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create(['username' => 'firstuser']);
$employeeUser->userProfile()->create(['full_name' => 'First', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$other = User::factory()->create(['username' => 'seconduser']);
$response = $this->put(route('admin.hr.employees.update', $employeeUser), [
'email' => $employeeUser->email,
'username' => 'seconduser',
'full_name' => 'First',
'join_date' => '2024-01-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertSessionHasErrors('username');
});
test('updating non-existent employee returns 404', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->put(route('admin.hr.employees.update', 999999), [
'email' => 'ghost@example.com',
'username' => 'ghost',
'full_name' => 'Ghost',
'join_date' => '2024-01-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertStatus(404);
});
/*
|--------------------------------------------------------------------------
| DELETE / DESTROY
|--------------------------------------------------------------------------
*/
test('employee can be deleted', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create();
$employeeUser->userProfile()->create(['full_name' => 'To Delete', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->delete(route('admin.hr.employees.destroy', $employeeUser));
$response
->assertSessionHasNoErrors()
->assertRedirect(route('admin.hr.employees.index'));
$this->assertSoftDeleted('users', ['id' => $employeeUser->id]);
$this->assertSoftDeleted('user_profiles', ['user_id' => $employeeUser->id]);
$this->assertSoftDeleted('employees', ['user_id' => $employeeUser->id]);
});
test('delete flashes success toast via Inertia', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create();
$employeeUser->userProfile()->create(['full_name' => 'Toast Delete', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->delete(route('admin.hr.employees.destroy', $employeeUser));
$response->assertRedirect();
});
test('deleting non-existent employee returns 404', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->delete(route('admin.hr.employees.destroy', 999999));
$response->assertStatus(404);
});
/*
|--------------------------------------------------------------------------
| TOGGLE ACTIVE
|--------------------------------------------------------------------------
*/
test('employee can be toggled active', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create(['is_active' => true]);
$employeeUser->userProfile()->create(['full_name' => 'Toggle Me', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->post(route('admin.hr.employees.toggle-active', $employeeUser));
$response->assertRedirect();
$employeeUser->refresh();
expect($employeeUser->is_active)->toBeFalse();
});
test('employee can be toggled back to active', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create(['is_active' => false]);
$employeeUser->userProfile()->create(['full_name' => 'Toggle Back', 'gender' => 'female']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$this->post(route('admin.hr.employees.toggle-active', $employeeUser));
$employeeUser->refresh();
expect($employeeUser->is_active)->toBeTrue();
});
/*
|--------------------------------------------------------------------------
| RESET PASSWORD
|--------------------------------------------------------------------------
*/
test('employee password can be reset', function () {
$user = User::factory()->create();
$this->actingAs($user);
$employeeUser = User::factory()->create(['password' => 'old-password']);
$employeeUser->userProfile()->create(['full_name' => 'Reset Me', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->post(route('admin.hr.employees.reset-password', $employeeUser));
$response->assertRedirect();
$employeeUser->refresh();
expect($employeeUser->password)->not->toBe('old-password');
});
/*
|--------------------------------------------------------------------------
| AUTHORIZATION - GUEST CANNOT PERFORM ACTIONS
|--------------------------------------------------------------------------
*/
test('guest cannot create employee', function () {
$response = $this->post(route('admin.hr.employees.store'), [
'email' => 'unauthorized@example.com',
'username' => 'unauthorized',
'full_name' => 'Unauthorized',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertRedirect(route('login'));
});
test('guest cannot update employee', function () {
$employeeUser = User::factory()->create();
$employeeUser->userProfile()->create(['full_name' => 'Protected', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->put(route('admin.hr.employees.update', $employeeUser), [
'email' => 'hacked@example.com',
'username' => 'hacked',
'full_name' => 'Hacked',
'join_date' => '2024-01-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$response->assertRedirect(route('login'));
});
test('guest cannot delete employee', function () {
$employeeUser = User::factory()->create();
$employeeUser->userProfile()->create(['full_name' => 'Protected', 'gender' => 'male']);
$employeeUser->employee()->create(['join_date' => '2024-01-01', 'employment_status' => 'full_time', 'base_salary' => 5000000]);
$response = $this->delete(route('admin.hr.employees.destroy', $employeeUser));
$response->assertRedirect(route('login'));
$this->assertDatabaseHas('users', ['id' => $employeeUser->id]);
});
/*
|--------------------------------------------------------------------------
| DATA INTEGRITY
|--------------------------------------------------------------------------
*/
test('created employee has correct timestamps', function () {
$user = User::factory()->create();
$this->actingAs($user);
$this->post(route('admin.hr.employees.store'), [
'email' => 'timestamp@example.com',
'username' => 'timestamp',
'full_name' => 'Timestamp Employee',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$employeeUser = User::where('email', 'timestamp@example.com')->first();
expect($employeeUser->created_at)->not->toBeNull();
expect($employeeUser->employee->created_at)->not->toBeNull();
});
test('employee factory creates valid employee', function () {
$employeeUser = User::factory()->create();
$employeeUser->userProfile()->create(['full_name' => fake()->name(), 'gender' => 'male']);
$employeeUser->employee()->create([
'join_date' => fake()->date(),
'employment_status' => fake()->randomElement(['full_time', 'part_time', 'contract', 'internship', 'resigned']),
'base_salary' => fake()->numberBetween(1000000, 10000000),
]);
expect($employeeUser->employee)->not->toBeNull();
expect($employeeUser->userProfile)->not->toBeNull();
});
/*
|--------------------------------------------------------------------------
| REALISTIC USER SCENARIOS
|--------------------------------------------------------------------------
*/
test('user creates employee then immediately edits it', function () {
$user = User::factory()->create();
$this->actingAs($user);
$this->post(route('admin.hr.employees.store'), [
'email' => 'draft@example.com',
'username' => 'draftemp',
'full_name' => 'Draft Employee',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$employeeUser = User::where('email', 'draft@example.com')->first();
$this->put(route('admin.hr.employees.update', $employeeUser), [
'email' => 'final@example.com',
'username' => 'draftemp',
'full_name' => 'Final Employee',
'join_date' => '2024-06-01',
'employment_status' => 'full_time',
'base_salary' => 6000000,
]);
$employeeUser->refresh();
expect($employeeUser->email)->toBe('final@example.com');
expect($employeeUser->userProfile->full_name)->toBe('Final Employee');
expect($employeeUser->employee->base_salary)->toBe(6000000);
});
test('user creates multiple employees and deletes one', function () {
$user = User::factory()->create();
$this->actingAs($user);
$this->post(route('admin.hr.employees.store'), [
'email' => 'emp1@example.com',
'username' => 'emp1',
'full_name' => 'Employee 1',
'join_date' => '2024-01-01',
'employment_status' => 'full_time',
'base_salary' => 5000000,
]);
$this->post(route('admin.hr.employees.store'), [
'email' => 'emp2@example.com',
'username' => 'emp2',
'full_name' => 'Employee 2',
'join_date' => '2024-01-01',
'employment_status' => 'part_time',
'base_salary' => 3000000,
]);
$toDelete = User::where('email', 'emp1@example.com')->first();
$this->delete(route('admin.hr.employees.destroy', $toDelete));
$response = $this->get(route('admin.hr.employees.index'));
$response->assertOk();
$response->assertInertia(fn (Assert $page) => $page
->component('admin/hr/employee/index')
->has('employees', 1)
);
});
test('user tries to create employee without submitting any data', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post(route('admin.hr.employees.store'), []);
$response->assertSessionHasErrors(['email', 'username', 'full_name', 'join_date', 'employment_status', 'base_salary']);
});