refactor: remove unused check_out_photo_path from checkedInOnly state in AttendanceFactory
This commit is contained in:
parent
f12c7cea75
commit
66bdd86b4f
@ -7,11 +7,11 @@
|
||||
use Illuminate\Database\Eloquent\Attributes\Appends;
|
||||
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
#[Guarded(['id'])]
|
||||
#[Appends(['amount_formatted', 'type_label', 'created_at_formatted', 'created_by_name'])]
|
||||
|
||||
@ -33,7 +33,6 @@ public function checkedInOnly(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'check_out_at' => null,
|
||||
'check_out_photo_path' => null,
|
||||
'check_out_latitude' => null,
|
||||
'check_out_longitude' => null,
|
||||
'work_duration_minutes' => null,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\CashTransactionType;
|
||||
use App\Enums\Permission as PermissionEnum;
|
||||
use App\Models\CashAccount;
|
||||
use App\Models\CashTransaction;
|
||||
|
||||
@ -4,13 +4,11 @@
|
||||
use App\Enums\PayrollPeriodStatus;
|
||||
use App\Enums\PayrollStatus;
|
||||
use App\Enums\Permission as PermissionEnum;
|
||||
use App\Models\CashAccount;
|
||||
use App\Models\Employee;
|
||||
use App\Models\Payroll;
|
||||
use App\Models\PayrollAdjustment;
|
||||
use App\Models\PayrollPeriod;
|
||||
use App\Models\User;
|
||||
use App\Models\UserProfile;
|
||||
use Database\Seeders\CashAccountSeeder;
|
||||
use Database\Seeders\RolePermissionSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
508
tests/Feature/Admin/Hr/AttendanceTest.php
Normal file
508
tests/Feature/Admin/Hr/AttendanceTest.php
Normal file
@ -0,0 +1,508 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\Permission as PermissionEnum;
|
||||
use App\Models\Attendance;
|
||||
use App\Models\Employee;
|
||||
use App\Models\User;
|
||||
use App\Models\UserProfile;
|
||||
use Carbon\CarbonInterface;
|
||||
use Database\Seeders\RolePermissionSeeder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->seed(RolePermissionSeeder::class);
|
||||
Storage::fake('public');
|
||||
Queue::fake();
|
||||
});
|
||||
|
||||
// ─── Helper ───────────────────────────────────────────────
|
||||
|
||||
function createAttendanceUserWithPermission(PermissionEnum ...$permissions): User
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$user->givePermissionTo(
|
||||
array_merge(
|
||||
[PermissionEnum::DASHBOARD_VIEW->value],
|
||||
array_map(fn (PermissionEnum $p) => $p->value, $permissions)
|
||||
)
|
||||
);
|
||||
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
function createAttendanceEmployeeUser(PermissionEnum ...$permissions): User
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
UserProfile::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'full_name' => fake()->name(),
|
||||
]);
|
||||
Employee::factory()->create(['user_id' => $user->id]);
|
||||
$user->assignRole('marketing');
|
||||
|
||||
$user->givePermissionTo(
|
||||
array_merge(
|
||||
[PermissionEnum::DASHBOARD_VIEW->value],
|
||||
array_map(fn (PermissionEnum $p) => $p->value, $permissions)
|
||||
)
|
||||
);
|
||||
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
function fakeBase64Photo(): string
|
||||
{
|
||||
// 1x1 pixel transparent PNG
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
|
||||
}
|
||||
|
||||
function checkInPayload(): array
|
||||
{
|
||||
return [
|
||||
'photo' => fakeBase64Photo(),
|
||||
'latitude' => -6.2088,
|
||||
'longitude' => 106.8456,
|
||||
];
|
||||
}
|
||||
|
||||
function checkOutPayload(): array
|
||||
{
|
||||
return [
|
||||
'photo' => fakeBase64Photo(),
|
||||
'latitude' => -6.2090,
|
||||
'longitude' => 106.8460,
|
||||
];
|
||||
}
|
||||
|
||||
function createTodayAttendanceForEmployee(Employee $employee): Attendance
|
||||
{
|
||||
return Attendance::factory()->create([
|
||||
'employee_id' => $employee->id,
|
||||
'attendance_date' => today()->format('Y-m-d'),
|
||||
'check_in_at' => now()->subHours(8),
|
||||
'check_out_at' => null,
|
||||
'check_out_latitude' => null,
|
||||
'check_out_longitude' => null,
|
||||
'work_duration_minutes' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
// ─── Index ────────────────────────────────────────────────
|
||||
|
||||
describe('Attendance Index', function () {
|
||||
test('authenticated user with permission can view attendance index', function () {
|
||||
$user = createAttendanceUserWithPermission(PermissionEnum::ATTENDANCES_VIEW);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.attendances.index'))
|
||||
->assertOk();
|
||||
});
|
||||
|
||||
test('guest is redirected to login', function () {
|
||||
$this->get(route('admin.hr.attendances.index'))
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without permission is forbidden', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.attendances.index'))
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('index displays attendances for employee user', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW);
|
||||
|
||||
Attendance::factory()->count(3)->create([
|
||||
'employee_id' => $user->employee->id,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.attendances.index'))
|
||||
->assertOk();
|
||||
});
|
||||
|
||||
test('manager can see all attendances', function () {
|
||||
$user = createAttendanceUserWithPermission(PermissionEnum::ATTENDANCES_VIEW);
|
||||
$user->assignRole('owner');
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.attendances.index'))
|
||||
->assertOk();
|
||||
});
|
||||
|
||||
test('index accepts date range parameters', function () {
|
||||
$user = createAttendanceUserWithPermission(PermissionEnum::ATTENDANCES_VIEW);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.attendances.index', [
|
||||
'start' => now()->startOfMonth()->format('Y-m-d'),
|
||||
'end' => now()->endOfMonth()->format('Y-m-d'),
|
||||
]))
|
||||
->assertOk();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Check In ─────────────────────────────────────────────
|
||||
|
||||
describe('Attendance Check In', function () {
|
||||
test('employee with permission can check in', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), checkInPayload())
|
||||
->assertRedirect(route('admin.hr.attendances.index'));
|
||||
|
||||
$this->assertDatabaseHas('attendances', [
|
||||
'employee_id' => $user->employee->id,
|
||||
'attendance_date' => today()->format('Y-m-d'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('guest cannot check in', function () {
|
||||
$this->post(route('admin.hr.attendances.check_in'), checkInPayload())
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without create permission is forbidden', function () {
|
||||
$user = createAttendanceUserWithPermission(PermissionEnum::ATTENDANCES_VIEW);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), checkInPayload())
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('user without employee record cannot check in', function () {
|
||||
$user = createAttendanceUserWithPermission(
|
||||
PermissionEnum::ATTENDANCES_VIEW,
|
||||
PermissionEnum::ATTENDANCES_CREATE,
|
||||
);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), checkInPayload())
|
||||
->assertStatus(302);
|
||||
});
|
||||
|
||||
test('cannot check in twice on the same day', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
createTodayAttendanceForEmployee($user->employee);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), checkInPayload())
|
||||
->assertSessionHasErrors('attendance');
|
||||
});
|
||||
|
||||
test('photo is required for check in', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$payload = checkInPayload();
|
||||
$payload['photo'] = '';
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), $payload)
|
||||
->assertSessionHasErrors('photo');
|
||||
});
|
||||
|
||||
test('latitude is required for check in', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$payload = checkInPayload();
|
||||
unset($payload['latitude']);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), $payload)
|
||||
->assertSessionHasErrors('latitude');
|
||||
});
|
||||
|
||||
test('longitude is required for check in', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$payload = checkInPayload();
|
||||
unset($payload['longitude']);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), $payload)
|
||||
->assertSessionHasErrors('longitude');
|
||||
});
|
||||
|
||||
test('latitude must be between -90 and 90', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$payload = checkInPayload();
|
||||
$payload['latitude'] = 100;
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), $payload)
|
||||
->assertSessionHasErrors('latitude');
|
||||
});
|
||||
|
||||
test('longitude must be between -180 and 180', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$payload = checkInPayload();
|
||||
$payload['longitude'] = 200;
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), $payload)
|
||||
->assertSessionHasErrors('longitude');
|
||||
});
|
||||
|
||||
test('check in stores latitude and longitude', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_in'), [
|
||||
'photo' => fakeBase64Photo(),
|
||||
'latitude' => -6.2088,
|
||||
'longitude' => 106.8456,
|
||||
]);
|
||||
|
||||
$attendance = Attendance::where('employee_id', $user->employee->id)->first();
|
||||
expect($attendance)->not->toBeNull();
|
||||
expect($attendance->check_in_at)->not->toBeNull();
|
||||
expect($attendance->check_out_at)->toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Check Out ────────────────────────────────────────────
|
||||
|
||||
describe('Attendance Check Out', function () {
|
||||
test('employee with permission can check out', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
createTodayAttendanceForEmployee($user->employee);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_out'), checkOutPayload())
|
||||
->assertRedirect(route('admin.hr.attendances.index'));
|
||||
|
||||
$attendance = Attendance::where('employee_id', $user->employee->id)
|
||||
->whereDate('attendance_date', today())
|
||||
->first();
|
||||
|
||||
expect($attendance->check_out_at)->not->toBeNull();
|
||||
expect($attendance->work_duration_minutes)->not->toBeNull();
|
||||
});
|
||||
|
||||
test('guest cannot check out', function () {
|
||||
$this->post(route('admin.hr.attendances.check_out'), checkOutPayload())
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without create permission is forbidden', function () {
|
||||
$user = createAttendanceUserWithPermission(PermissionEnum::ATTENDANCES_VIEW);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_out'), checkOutPayload())
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('cannot check out without checking in first', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_out'), checkOutPayload())
|
||||
->assertSessionHasErrors('attendance');
|
||||
});
|
||||
|
||||
test('cannot check out twice on the same day', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
$attendance = createTodayAttendanceForEmployee($user->employee);
|
||||
$attendance->update([
|
||||
'check_out_at' => now(),
|
||||
'work_duration_minutes' => 480,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_out'), checkOutPayload())
|
||||
->assertSessionHasErrors('attendance');
|
||||
});
|
||||
|
||||
test('photo is required for check out', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
createTodayAttendanceForEmployee($user->employee);
|
||||
|
||||
$payload = checkOutPayload();
|
||||
$payload['photo'] = '';
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_out'), $payload)
|
||||
->assertSessionHasErrors('photo');
|
||||
});
|
||||
|
||||
test('check out calculates work duration', function () {
|
||||
$user = createAttendanceEmployeeUser(PermissionEnum::ATTENDANCES_VIEW, PermissionEnum::ATTENDANCES_CREATE);
|
||||
|
||||
Attendance::factory()->create([
|
||||
'employee_id' => $user->employee->id,
|
||||
'attendance_date' => today()->format('Y-m-d'),
|
||||
'check_in_at' => now()->subHours(8),
|
||||
'check_out_at' => null,
|
||||
'check_out_latitude' => null,
|
||||
'check_out_longitude' => null,
|
||||
'work_duration_minutes' => null,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.attendances.check_out'), checkOutPayload());
|
||||
|
||||
$attendance = Attendance::where('employee_id', $user->employee->id)
|
||||
->whereDate('attendance_date', today())
|
||||
->first();
|
||||
|
||||
expect($attendance->work_duration_minutes)->toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Destroy ──────────────────────────────────────────────
|
||||
|
||||
describe('Attendance Destroy', function () {
|
||||
test('user with permission can delete attendance', function () {
|
||||
$user = createAttendanceUserWithPermission(
|
||||
PermissionEnum::ATTENDANCES_VIEW,
|
||||
PermissionEnum::ATTENDANCES_DELETE,
|
||||
);
|
||||
|
||||
$attendance = Attendance::factory()->create();
|
||||
|
||||
$this->actingAs($user)
|
||||
->delete(route('admin.hr.attendances.destroy', $attendance))
|
||||
->assertRedirect(route('admin.hr.attendances.index'));
|
||||
|
||||
$this->assertDatabaseMissing('attendances', ['id' => $attendance->id]);
|
||||
});
|
||||
|
||||
test('guest cannot delete attendance', function () {
|
||||
$attendance = Attendance::factory()->create();
|
||||
|
||||
$this->delete(route('admin.hr.attendances.destroy', $attendance))
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without delete permission is forbidden', function () {
|
||||
$user = createAttendanceUserWithPermission(PermissionEnum::ATTENDANCES_VIEW);
|
||||
|
||||
$attendance = Attendance::factory()->create();
|
||||
|
||||
$this->actingAs($user)
|
||||
->delete(route('admin.hr.attendances.destroy', $attendance))
|
||||
->assertForbidden();
|
||||
|
||||
$this->assertDatabaseHas('attendances', ['id' => $attendance->id]);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Attendance Model ─────────────────────────────────────
|
||||
|
||||
describe('Attendance Model', function () {
|
||||
test('attendance has correct casts', function () {
|
||||
$attendance = Attendance::factory()->create([
|
||||
'check_in_latitude' => -6.2088000,
|
||||
'check_in_longitude' => 106.8456000,
|
||||
]);
|
||||
|
||||
expect($attendance->attendance_date)->toBeInstanceOf(CarbonInterface::class);
|
||||
expect($attendance->check_in_at)->toBeInstanceOf(CarbonInterface::class);
|
||||
expect($attendance->check_out_at)->toBeInstanceOf(CarbonInterface::class);
|
||||
expect($attendance->work_duration_minutes)->toBeInt();
|
||||
});
|
||||
|
||||
test('attendance belongs to employee', function () {
|
||||
$attendance = Attendance::factory()->create();
|
||||
|
||||
expect($attendance->employee)->not->toBeNull();
|
||||
expect($attendance->employee)->toBeInstanceOf(Employee::class);
|
||||
});
|
||||
|
||||
test('attendance has check_in_at_formatted accessor', function () {
|
||||
$attendance = Attendance::factory()->create([
|
||||
'check_in_at' => '2025-06-15 08:00:00',
|
||||
]);
|
||||
|
||||
expect($attendance->check_in_at_formatted)->toBeString();
|
||||
});
|
||||
|
||||
test('attendance has check_out_at_formatted accessor', function () {
|
||||
$attendance = Attendance::factory()->create([
|
||||
'check_out_at' => '2025-06-15 17:00:00',
|
||||
]);
|
||||
|
||||
expect($attendance->check_out_at_formatted)->toBeString();
|
||||
});
|
||||
|
||||
test('attendance has attendance_date_formatted accessor', function () {
|
||||
$attendance = Attendance::factory()->create([
|
||||
'attendance_date' => '2025-06-15',
|
||||
]);
|
||||
|
||||
expect($attendance->attendance_date_formatted)->toBeString();
|
||||
});
|
||||
|
||||
test('attendance has employee_name accessor', function () {
|
||||
$employee = Employee::factory()->create();
|
||||
UserProfile::factory()->create([
|
||||
'user_id' => $employee->user_id,
|
||||
'full_name' => 'John Doe',
|
||||
]);
|
||||
|
||||
$attendance = Attendance::factory()->create([
|
||||
'employee_id' => $employee->id,
|
||||
]);
|
||||
|
||||
expect($attendance->employee_name)->toBe('John Doe');
|
||||
});
|
||||
|
||||
test('attendance has work_duration_formatted accessor with hours', function () {
|
||||
$attendance = Attendance::factory()->create([
|
||||
'work_duration_minutes' => 480,
|
||||
]);
|
||||
|
||||
expect($attendance->work_duration_formatted)->toBe('8 jam 0 menit');
|
||||
});
|
||||
|
||||
test('attendance has work_duration_formatted accessor with minutes only', function () {
|
||||
$attendance = Attendance::factory()->create([
|
||||
'work_duration_minutes' => 45,
|
||||
]);
|
||||
|
||||
expect($attendance->work_duration_formatted)->toBe('45 menit');
|
||||
});
|
||||
|
||||
test('attendance has null work_duration_formatted when no checkout', function () {
|
||||
$attendance = Attendance::factory()->create([
|
||||
'work_duration_minutes' => null,
|
||||
]);
|
||||
|
||||
expect($attendance->work_duration_formatted)->toBeNull();
|
||||
});
|
||||
|
||||
test('checkedInOnly factory state works', function () {
|
||||
$attendance = Attendance::factory()->checkedInOnly()->create();
|
||||
|
||||
expect($attendance->check_out_at)->toBeNull();
|
||||
expect($attendance->check_out_latitude)->toBeNull();
|
||||
expect($attendance->check_out_longitude)->toBeNull();
|
||||
expect($attendance->work_duration_minutes)->toBeNull();
|
||||
});
|
||||
|
||||
test('attendance has payroll_adjustments relationship', function () {
|
||||
$attendance = Attendance::factory()->create();
|
||||
|
||||
expect($attendance->payrollAdjustments())->toBeInstanceOf(HasMany::class);
|
||||
});
|
||||
});
|
||||
618
tests/Feature/Admin/Hr/LeaveRequestTest.php
Normal file
618
tests/Feature/Admin/Hr/LeaveRequestTest.php
Normal file
@ -0,0 +1,618 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\LeaveRequestStatus;
|
||||
use App\Enums\Permission as PermissionEnum;
|
||||
use App\Models\Employee;
|
||||
use App\Models\LeaveRequest;
|
||||
use App\Models\User;
|
||||
use App\Models\UserProfile;
|
||||
use Carbon\CarbonInterface;
|
||||
use Database\Seeders\RolePermissionSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->seed(RolePermissionSeeder::class);
|
||||
Queue::fake();
|
||||
});
|
||||
|
||||
// ─── Helper ───────────────────────────────────────────────
|
||||
|
||||
function createLeaveRequestUserWithPermission(PermissionEnum ...$permissions): User
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$user->givePermissionTo(
|
||||
array_merge(
|
||||
[PermissionEnum::DASHBOARD_VIEW->value],
|
||||
array_map(fn (PermissionEnum $p) => $p->value, $permissions)
|
||||
)
|
||||
);
|
||||
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
function createLeaveEmployeeUser(): User
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
UserProfile::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'full_name' => fake()->name(),
|
||||
]);
|
||||
Employee::factory()->create(['user_id' => $user->id]);
|
||||
$user->assignRole('marketing');
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
function createEmployeeUserWithLeavePermission(PermissionEnum ...$permissions): User
|
||||
{
|
||||
$user = createLeaveEmployeeUser();
|
||||
|
||||
$user->givePermissionTo(
|
||||
array_merge(
|
||||
[PermissionEnum::DASHBOARD_VIEW->value],
|
||||
array_map(fn (PermissionEnum $p) => $p->value, $permissions)
|
||||
)
|
||||
);
|
||||
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
function leaveRequestPayload(): array
|
||||
{
|
||||
return [
|
||||
'start_date' => now()->addDays(2)->format('Y-m-d'),
|
||||
'end_date' => now()->addDays(4)->format('Y-m-d'),
|
||||
];
|
||||
}
|
||||
|
||||
// ─── Index ────────────────────────────────────────────────
|
||||
|
||||
describe('Leave Request Index', function () {
|
||||
test('authenticated user with permission can view leave request index', function () {
|
||||
$user = createLeaveRequestUserWithPermission(PermissionEnum::LEAVE_REQUESTS_VIEW);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.leave_requests.index'))
|
||||
->assertOk();
|
||||
});
|
||||
|
||||
test('guest is redirected to login', function () {
|
||||
$this->get(route('admin.hr.leave_requests.index'))
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without permission is forbidden', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.leave_requests.index'))
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('index displays leave requests for manager role', function () {
|
||||
$user = createLeaveRequestUserWithPermission(PermissionEnum::LEAVE_REQUESTS_VIEW);
|
||||
|
||||
LeaveRequest::factory()->count(3)->create();
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.leave_requests.index'))
|
||||
->assertOk();
|
||||
});
|
||||
|
||||
test('employee only sees own leave requests', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
$ownRequest = LeaveRequest::factory()->create([
|
||||
'employee_id' => $user->employee->id,
|
||||
]);
|
||||
|
||||
$otherRequest = LeaveRequest::factory()->create();
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('admin.hr.leave_requests.index'))
|
||||
->assertOk();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Store ────────────────────────────────────────────────
|
||||
|
||||
describe('Leave Request Store', function () {
|
||||
test('employee with permission can submit leave request', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), leaveRequestPayload())
|
||||
->assertRedirect(route('admin.hr.leave_requests.index'));
|
||||
|
||||
$this->assertDatabaseHas('leave_requests', [
|
||||
'employee_id' => $user->employee->id,
|
||||
'status' => LeaveRequestStatus::PENDING->value,
|
||||
]);
|
||||
});
|
||||
|
||||
test('guest cannot submit leave request', function () {
|
||||
$this->post(route('admin.hr.leave_requests.store'), leaveRequestPayload())
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without create permission is forbidden', function () {
|
||||
$user = createLeaveRequestUserWithPermission(PermissionEnum::LEAVE_REQUESTS_VIEW);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), leaveRequestPayload())
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('user without employee record cannot submit leave request', function () {
|
||||
$user = createLeaveRequestUserWithPermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), leaveRequestPayload())
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('start_date is required', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
$payload = leaveRequestPayload();
|
||||
unset($payload['start_date']);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), $payload)
|
||||
->assertSessionHasErrors('start_date');
|
||||
});
|
||||
|
||||
test('end_date is required', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
$payload = leaveRequestPayload();
|
||||
unset($payload['end_date']);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), $payload)
|
||||
->assertSessionHasErrors('end_date');
|
||||
});
|
||||
|
||||
test('end_date must be after or equal to start_date', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
$payload = [
|
||||
'start_date' => now()->addDays(5)->format('Y-m-d'),
|
||||
'end_date' => now()->addDays(2)->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), $payload)
|
||||
->assertSessionHasErrors('end_date');
|
||||
});
|
||||
|
||||
test('cannot submit when there is a pending leave request', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
LeaveRequest::factory()->create([
|
||||
'employee_id' => $user->employee->id,
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), leaveRequestPayload())
|
||||
->assertSessionHasErrors('start_date');
|
||||
});
|
||||
|
||||
test('submitting leave request calculates total_days correctly', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
$payload = [
|
||||
'start_date' => now()->addDays(2)->format('Y-m-d'),
|
||||
'end_date' => now()->addDays(5)->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), $payload);
|
||||
|
||||
$leaveRequest = LeaveRequest::where('employee_id', $user->employee->id)->first();
|
||||
expect($leaveRequest->total_days)->toBe(4);
|
||||
});
|
||||
|
||||
test('submitting leave request creates with pending status', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_CREATE,
|
||||
);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.store'), leaveRequestPayload());
|
||||
|
||||
$leaveRequest = LeaveRequest::where('employee_id', $user->employee->id)->first();
|
||||
expect($leaveRequest->status)->toBe(LeaveRequestStatus::PENDING);
|
||||
expect($leaveRequest->verified_at)->toBeNull();
|
||||
expect($leaveRequest->verified_by_id)->toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Update ───────────────────────────────────────────────
|
||||
|
||||
describe('Leave Request Update', function () {
|
||||
test('employee with permission can update own pending leave request', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_UPDATE,
|
||||
);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'employee_id' => $user->employee->id,
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$payload = [
|
||||
'start_date' => now()->addDays(10)->format('Y-m-d'),
|
||||
'end_date' => now()->addDays(12)->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$this->actingAs($user)
|
||||
->put(route('admin.hr.leave_requests.update', $leaveRequest), $payload)
|
||||
->assertRedirect(route('admin.hr.leave_requests.index'));
|
||||
|
||||
expect($leaveRequest->fresh()->total_days)->toBe(3);
|
||||
});
|
||||
|
||||
test('guest cannot update leave request', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create();
|
||||
|
||||
$this->put(route('admin.hr.leave_requests.update', $leaveRequest), leaveRequestPayload())
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without update permission is forbidden', function () {
|
||||
$user = createLeaveRequestUserWithPermission(PermissionEnum::LEAVE_REQUESTS_VIEW);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'employee_id' => Employee::factory()->create()->id,
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->put(route('admin.hr.leave_requests.update', $leaveRequest), leaveRequestPayload())
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('employee cannot update other employee leave request', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_UPDATE,
|
||||
);
|
||||
|
||||
$otherLeaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->put(route('admin.hr.leave_requests.update', $otherLeaveRequest), leaveRequestPayload())
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('cannot update approved leave request', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_UPDATE,
|
||||
);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'employee_id' => $user->employee->id,
|
||||
'status' => LeaveRequestStatus::APPROVED,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->put(route('admin.hr.leave_requests.update', $leaveRequest), leaveRequestPayload())
|
||||
->assertForbidden();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Destroy ──────────────────────────────────────────────
|
||||
|
||||
describe('Leave Request Destroy', function () {
|
||||
test('employee with permission can delete own leave request', function () {
|
||||
$user = createEmployeeUserWithLeavePermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_DELETE,
|
||||
);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'employee_id' => $user->employee->id,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->delete(route('admin.hr.leave_requests.destroy', $leaveRequest))
|
||||
->assertRedirect(route('admin.hr.leave_requests.index'));
|
||||
|
||||
$this->assertDatabaseMissing('leave_requests', ['id' => $leaveRequest->id]);
|
||||
});
|
||||
|
||||
test('guest cannot delete leave request', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create();
|
||||
|
||||
$this->delete(route('admin.hr.leave_requests.destroy', $leaveRequest))
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without delete permission is forbidden', function () {
|
||||
$user = createLeaveRequestUserWithPermission(PermissionEnum::LEAVE_REQUESTS_VIEW);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create();
|
||||
|
||||
$this->actingAs($user)
|
||||
->delete(route('admin.hr.leave_requests.destroy', $leaveRequest))
|
||||
->assertForbidden();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Approve ──────────────────────────────────────────────
|
||||
|
||||
describe('Leave Request Approve', function () {
|
||||
test('user with verify permission can approve leave request', function () {
|
||||
$user = createLeaveRequestUserWithPermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_VERIFY,
|
||||
);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.approve', $leaveRequest))
|
||||
->assertRedirect(route('admin.hr.leave_requests.index'));
|
||||
|
||||
expect($leaveRequest->fresh()->status)->toBe(LeaveRequestStatus::APPROVED);
|
||||
expect($leaveRequest->fresh()->verified_by_id)->toBe($user->id);
|
||||
expect($leaveRequest->fresh()->verified_at)->not->toBeNull();
|
||||
});
|
||||
|
||||
test('guest cannot approve leave request', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create();
|
||||
|
||||
$this->post(route('admin.hr.leave_requests.approve', $leaveRequest))
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without verify permission is forbidden', function () {
|
||||
$user = createLeaveRequestUserWithPermission(PermissionEnum::LEAVE_REQUESTS_VIEW);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.approve', $leaveRequest))
|
||||
->assertForbidden();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Reject ───────────────────────────────────────────────
|
||||
|
||||
describe('Leave Request Reject', function () {
|
||||
test('user with verify permission can reject leave request', function () {
|
||||
$user = createLeaveRequestUserWithPermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_VERIFY,
|
||||
);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.reject', $leaveRequest), [
|
||||
'reason' => 'Tidak ada pengganti',
|
||||
])
|
||||
->assertRedirect(route('admin.hr.leave_requests.index'));
|
||||
|
||||
expect($leaveRequest->fresh()->status)->toBe(LeaveRequestStatus::REJECTED);
|
||||
expect($leaveRequest->fresh()->verified_by_id)->toBe($user->id);
|
||||
expect($leaveRequest->fresh()->verified_at)->not->toBeNull();
|
||||
});
|
||||
|
||||
test('rejecting creates rejection record', function () {
|
||||
$user = createLeaveRequestUserWithPermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_VERIFY,
|
||||
);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.reject', $leaveRequest), [
|
||||
'reason' => 'Stok karyawan kurang',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('rejections', [
|
||||
'rejectable_type' => LeaveRequest::class,
|
||||
'rejectable_id' => $leaveRequest->id,
|
||||
'reason' => 'Stok karyawan kurang',
|
||||
'rejected_by_id' => $user->id,
|
||||
]);
|
||||
});
|
||||
|
||||
test('guest cannot reject leave request', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create();
|
||||
|
||||
$this->post(route('admin.hr.leave_requests.reject', $leaveRequest), [
|
||||
'reason' => 'Test',
|
||||
])->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
test('user without verify permission is forbidden', function () {
|
||||
$user = createLeaveRequestUserWithPermission(PermissionEnum::LEAVE_REQUESTS_VIEW);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.reject', $leaveRequest), [
|
||||
'reason' => 'Test',
|
||||
])
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('reason is required when rejecting', function () {
|
||||
$user = createLeaveRequestUserWithPermission(
|
||||
PermissionEnum::LEAVE_REQUESTS_VIEW,
|
||||
PermissionEnum::LEAVE_REQUESTS_VERIFY,
|
||||
);
|
||||
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('admin.hr.leave_requests.reject', $leaveRequest), [
|
||||
'reason' => '',
|
||||
])
|
||||
->assertSessionHasErrors('reason');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Leave Request Model ──────────────────────────────────
|
||||
|
||||
describe('Leave Request Model', function () {
|
||||
test('leave request has correct casts', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'total_days' => 5,
|
||||
]);
|
||||
|
||||
expect($leaveRequest->total_days)->toBeInt();
|
||||
expect($leaveRequest->start_date)->toBeInstanceOf(CarbonInterface::class);
|
||||
expect($leaveRequest->end_date)->toBeInstanceOf(CarbonInterface::class);
|
||||
});
|
||||
|
||||
test('leave request belongs to employee', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create();
|
||||
|
||||
expect($leaveRequest->employee)->not->toBeNull();
|
||||
expect($leaveRequest->employee)->toBeInstanceOf(Employee::class);
|
||||
});
|
||||
|
||||
test('leave request has status_label accessor', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
expect($leaveRequest->status_label)->toBe('Menunggu');
|
||||
|
||||
$leaveRequest->update(['status' => LeaveRequestStatus::APPROVED]);
|
||||
expect($leaveRequest->fresh()->status_label)->toBe('Disetujui');
|
||||
|
||||
$leaveRequest->update(['status' => LeaveRequestStatus::REJECTED]);
|
||||
expect($leaveRequest->fresh()->status_label)->toBe('Ditolak');
|
||||
});
|
||||
|
||||
test('leave request has start_date_formatted accessor', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'start_date' => '2025-06-15',
|
||||
]);
|
||||
|
||||
expect($leaveRequest->start_date_formatted)->toBeString();
|
||||
});
|
||||
|
||||
test('leave request has end_date_formatted accessor', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'end_date' => '2025-06-20',
|
||||
]);
|
||||
|
||||
expect($leaveRequest->end_date_formatted)->toBeString();
|
||||
});
|
||||
|
||||
test('leave request has start_date_input accessor', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'start_date' => '2025-06-15',
|
||||
]);
|
||||
|
||||
expect($leaveRequest->start_date_input)->toBe('2025-06-15');
|
||||
});
|
||||
|
||||
test('leave request has end_date_input accessor', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'end_date' => '2025-06-20',
|
||||
]);
|
||||
|
||||
expect($leaveRequest->end_date_input)->toBe('2025-06-20');
|
||||
});
|
||||
|
||||
test('leave request has is_editable accessor', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
expect($leaveRequest->is_editable)->toBeTrue();
|
||||
|
||||
$leaveRequest->update(['status' => LeaveRequestStatus::APPROVED]);
|
||||
expect($leaveRequest->fresh()->is_editable)->toBeFalse();
|
||||
});
|
||||
|
||||
test('leave request has can_verify accessor', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create([
|
||||
'status' => LeaveRequestStatus::PENDING,
|
||||
]);
|
||||
|
||||
expect($leaveRequest->can_verify)->toBeTrue();
|
||||
|
||||
$leaveRequest->update(['status' => LeaveRequestStatus::APPROVED]);
|
||||
expect($leaveRequest->fresh()->can_verify)->toBeFalse();
|
||||
});
|
||||
|
||||
test('leave request has employee_name accessor', function () {
|
||||
$leaveRequest = LeaveRequest::factory()->create();
|
||||
|
||||
expect($leaveRequest->employee_name)->toBeString();
|
||||
});
|
||||
|
||||
test('pending scope filters correctly', function () {
|
||||
LeaveRequest::factory()->create(['status' => LeaveRequestStatus::PENDING]);
|
||||
LeaveRequest::factory()->create(['status' => LeaveRequestStatus::APPROVED]);
|
||||
|
||||
expect(LeaveRequest::query()->pending()->count())->toBe(1);
|
||||
});
|
||||
|
||||
test('approved scope filters correctly', function () {
|
||||
LeaveRequest::factory()->create(['status' => LeaveRequestStatus::PENDING]);
|
||||
LeaveRequest::factory()->create(['status' => LeaveRequestStatus::APPROVED]);
|
||||
|
||||
expect(LeaveRequest::query()->approved()->count())->toBe(1);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user