1033 lines
37 KiB
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']);
|
|
});
|