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']); });