diff --git a/tests/Feature/Filament/Resources/Master/Departments/DepartmentResourceTest.php b/tests/Feature/Filament/Resources/Master/Departments/DepartmentResourceTest.php new file mode 100644 index 0000000..5e787da --- /dev/null +++ b/tests/Feature/Filament/Resources/Master/Departments/DepartmentResourceTest.php @@ -0,0 +1,222 @@ +user = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + // Setup Permissions + Permission::firstOrCreate(['name' => 'ViewAny:Department', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Create:Department', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Update:Department', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Delete:Department', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Restore:Department', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'ForceDelete:Department', 'guard_name' => 'web']); + + $this->user->givePermissionTo([ + 'ViewAny:Department', + 'Create:Department', + 'Update:Department', + 'Delete:Department', + 'Restore:Department', + 'ForceDelete:Department', + ]); +}); + +/* +|-------------------------------------------------------------------------- +| Positive Scenarios +|-------------------------------------------------------------------------- +*/ + +test('can render department list page', function () { + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->assertSuccessful(); +}); + +test('can list departments', function () { + $departments = Department::factory()->count(5)->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->call('loadTable') + ->assertCanSeeTableRecords($departments); +}); + +test('can create department', function () { + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->mountAction('create') + ->setActionData([ + 'name' => 'Dinas Pendidikan', + 'alias' => 'Disdik', + ]) + ->callMountedAction() + ->assertHasNoActionErrors(); + + $this->assertDatabaseHas('departments', [ + 'name' => 'Dinas Pendidikan', + 'alias' => 'Disdik', + 'is_active' => IsActive::ACTIVE->value, + ]); +}); + +test('can edit department', function () { + $department = Department::factory()->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->mountTableAction('edit', $department) + ->setActionData([ + 'name' => 'OPD Baru Updated', + 'alias' => 'OPD-U', + ]) + ->callMountedTableAction() + ->assertHasNoActionErrors(); + + expect($department->refresh()->name)->toBe('OPD Baru Updated'); +}); + +test('can toggle is_active status', function () { + $department = Department::factory()->create(['is_active' => IsActive::INACTIVE]); + + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->call('updateTableColumnState', 'is_active', $department->getKey(), true); + + expect($department->refresh()->is_active)->toBe(IsActive::ACTIVE); +}); + +/* +|-------------------------------------------------------------------------- +| Negative Scenarios (Validation & Authorization) +|-------------------------------------------------------------------------- +*/ + +test('cannot create department with invalid data', function () { + $this->actingAs($this->user); + + // Empty name & alias + Livewire::test(ManageDepartments::class) + ->mountAction('create') + ->setActionData(['name' => '', 'alias' => '']) + ->callMountedAction() + ->assertHasActionErrors(['name' => 'required', 'alias' => 'required']); + + // Name exceeding 100 chars + Livewire::test(ManageDepartments::class) + ->mountAction('create') + ->setActionData(['name' => str_repeat('a', 101), 'alias' => 'Short']) + ->callMountedAction() + ->assertHasActionErrors(['name' => 'max']); + + // Alias exceeding 20 chars + Livewire::test(ManageDepartments::class) + ->mountAction('create') + ->setActionData(['name' => 'Valid Name', 'alias' => str_repeat('a', 21)]) + ->callMountedAction() + ->assertHasActionErrors(['alias' => 'max']); +}); + +test('unauthorized user cannot render department page', function () { + $unauthorizedUser = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + $this->actingAs($unauthorizedUser); + + Livewire::test(ManageDepartments::class) + ->assertForbidden(); +}); + +test('user without permissions cannot see actions', function () { + $viewOnlyUser = User::factory()->create(['is_active' => IsActive::ACTIVE]); + $viewOnlyUser->givePermissionTo('ViewAny:Department'); + $department = Department::factory()->create(); + + $this->actingAs($viewOnlyUser); + + Livewire::test(ManageDepartments::class) + ->assertActionHidden('create') + ->assertTableActionHidden('edit', $department); +}); + +/* +|-------------------------------------------------------------------------- +| Soft Delete Scenarios +|-------------------------------------------------------------------------- +*/ + +test('can delete department', function () { + $department = Department::factory()->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->callTableAction('delete', $department); + + $this->assertSoftDeleted($department); +}); + +test('can restore deleted department', function () { + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + $department = Department::factory()->create(['deleted_at' => now()]); + + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->filterTable('trashed', 'with') + ->callTableAction('restore', $department); + + $this->assertDatabaseHas('departments', [ + 'id' => $department->id, + 'deleted_at' => null, + ]); +}); + +test('can force delete department', function () { + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + $department = Department::factory()->create(['deleted_at' => now()]); + + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->filterTable('trashed', 'only') + ->callTableAction('forceDelete', $department); + + $this->assertDatabaseMissing('departments', ['id' => $department->id]); +}); + +test('trashed filter visibility depends on developer role', function () { + $roleAdmin = Role::firstOrCreate(['name' => RoleEnum::ADMINISTRATOR->value, 'guard_name' => 'web']); + $this->user->assignRole($roleAdmin); + + $this->actingAs($this->user); + + Livewire::test(ManageDepartments::class) + ->assertTableFilterHidden('trashed'); + + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + Livewire::test(ManageDepartments::class) + ->assertTableFilterVisible('trashed'); +}); diff --git a/tests/Feature/Filament/Resources/Master/Themes/ThemeResourceTest.php b/tests/Feature/Filament/Resources/Master/Themes/ThemeResourceTest.php new file mode 100644 index 0000000..2596484 --- /dev/null +++ b/tests/Feature/Filament/Resources/Master/Themes/ThemeResourceTest.php @@ -0,0 +1,214 @@ +user = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + // Setup Permissions + Permission::firstOrCreate(['name' => 'ViewAny:Theme', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Create:Theme', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Update:Theme', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Delete:Theme', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Restore:Theme', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'ForceDelete:Theme', 'guard_name' => 'web']); + + $this->user->givePermissionTo([ + 'ViewAny:Theme', + 'Create:Theme', + 'Update:Theme', + 'Delete:Theme', + 'Restore:Theme', + 'ForceDelete:Theme', + ]); +}); + +/* +|-------------------------------------------------------------------------- +| Positive Scenarios +|-------------------------------------------------------------------------- +*/ + +test('can render theme list page', function () { + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->assertSuccessful(); +}); + +test('can list themes', function () { + $themes = Theme::factory()->count(5)->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->call('loadTable') + ->assertCanSeeTableRecords($themes); +}); + +test('can create theme', function () { + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->mountAction('create') + ->setActionData([ + 'name' => 'Tema Pendidikan', + ]) + ->callMountedAction() + ->assertHasNoActionErrors(); + + $this->assertDatabaseHas('themes', [ + 'name' => 'Tema Pendidikan', + 'is_active' => IsActive::ACTIVE->value, + ]); +}); + +test('can edit theme', function () { + $theme = Theme::factory()->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->mountTableAction('edit', $theme) + ->setActionData([ + 'name' => 'Tema Updated', + ]) + ->callMountedTableAction() + ->assertHasNoActionErrors(); + + expect($theme->refresh()->name)->toBe('Tema Updated'); +}); + +test('can toggle is_active status', function () { + $theme = Theme::factory()->create(['is_active' => IsActive::INACTIVE]); + + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->call('updateTableColumnState', 'is_active', $theme->getKey(), true); + + expect($theme->refresh()->is_active)->toBe(IsActive::ACTIVE); +}); + +/* +|-------------------------------------------------------------------------- +| Negative Scenarios (Validation & Authorization) +|-------------------------------------------------------------------------- +*/ + +test('cannot create theme with empty name', function () { + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->mountAction('create') + ->setActionData(['name' => '']) + ->callMountedAction() + ->assertHasActionErrors(['name' => 'required']); +}); + +test('cannot create theme with name exceeding 50 characters', function () { + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->mountAction('create') + ->setActionData(['name' => str_repeat('a', 51)]) + ->callMountedAction() + ->assertHasActionErrors(['name' => 'max']); +}); + +test('unauthorized user cannot render theme page', function () { + $unauthorizedUser = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + $this->actingAs($unauthorizedUser); + + Livewire::test(ManageThemes::class) + ->assertForbidden(); +}); + +test('user without permissions cannot see actions', function () { + $viewOnlyUser = User::factory()->create(['is_active' => IsActive::ACTIVE]); + $viewOnlyUser->givePermissionTo('ViewAny:Theme'); + $theme = Theme::factory()->create(); + + $this->actingAs($viewOnlyUser); + + Livewire::test(ManageThemes::class) + ->assertActionHidden('create') + ->assertTableActionHidden('edit', $theme); +}); + +/* +|-------------------------------------------------------------------------- +| Soft Delete Scenarios +|-------------------------------------------------------------------------- +*/ + +test('can delete theme', function () { + $theme = Theme::factory()->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->callTableAction('delete', $theme); + + $this->assertSoftDeleted($theme); +}); + +test('can restore deleted theme', function () { + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + $theme = Theme::factory()->create(['deleted_at' => now()]); + + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->filterTable('trashed', 'with') + ->callTableAction('restore', $theme); + + $this->assertDatabaseHas('themes', [ + 'id' => $theme->id, + 'deleted_at' => null, + ]); +}); + +test('can force delete theme', function () { + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + $theme = Theme::factory()->create(['deleted_at' => now()]); + + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->filterTable('trashed', 'only') + ->callTableAction('forceDelete', $theme); + + $this->assertDatabaseMissing('themes', ['id' => $theme->id]); +}); + +test('trashed filter visibility depends on developer role', function () { + $roleAdmin = Role::firstOrCreate(['name' => RoleEnum::ADMINISTRATOR->value, 'guard_name' => 'web']); + $this->user->assignRole($roleAdmin); + + $this->actingAs($this->user); + + Livewire::test(ManageThemes::class) + ->assertTableFilterHidden('trashed'); + + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + Livewire::test(ManageThemes::class) + ->assertTableFilterVisible('trashed'); +});