diff --git a/tests/Feature/Filament/Resources/Master/Classifications/ClassificationResourceTest.php b/tests/Feature/Filament/Resources/Master/Classifications/ClassificationResourceTest.php new file mode 100644 index 0000000..564bd75 --- /dev/null +++ b/tests/Feature/Filament/Resources/Master/Classifications/ClassificationResourceTest.php @@ -0,0 +1,199 @@ +user = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + // Setup Permissions + Permission::firstOrCreate(['name' => 'ViewAny:Classification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Create:Classification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Update:Classification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Delete:Classification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Restore:Classification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'ForceDelete:Classification', 'guard_name' => 'web']); + + $this->user->givePermissionTo([ + 'ViewAny:Classification', + 'Create:Classification', + 'Update:Classification', + 'Delete:Classification', + 'Restore:Classification', + 'ForceDelete:Classification', + ]); +}); + +test('can render classification list page', function () { + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->assertSuccessful(); +}); + +test('can list classifications', function () { + $classifications = Classification::factory()->count(5)->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->call('loadTable') + ->assertCanSeeTableRecords($classifications); +}); + +test('can create classification', function () { + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->mountAction('create') + ->setActionData([ + 'name' => 'Klasifikasi Baru', + ]) + ->callMountedAction() + ->assertHasNoActionErrors(); + + $this->assertDatabaseHas('classifications', [ + 'name' => 'Klasifikasi Baru', + 'is_active' => IsActive::ACTIVE->value, + ]); +}); + +test('cannot create classification with empty name', function () { + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->mountAction('create') + ->setActionData(['name' => '']) + ->callMountedAction() + ->assertHasActionErrors(['name' => 'required']); +}); + +test('cannot create classification with name exceeding 50 characters', function () { + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->mountAction('create') + ->setActionData(['name' => str_repeat('a', 51)]) + ->callMountedAction() + ->assertHasActionErrors(['name' => 'max']); +}); + +test('can edit classification', function () { + $classification = Classification::factory()->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->mountTableAction('edit', $classification) + ->setActionData([ + 'name' => 'Klasifikasi Updated', + ]) + ->callMountedTableAction() + ->assertHasNoActionErrors(); + + expect($classification->refresh()->name)->toBe('Klasifikasi Updated'); +}); + +test('can toggle is_active status', function () { + $classification = Classification::factory()->create(['is_active' => IsActive::INACTIVE]); + + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->call('updateTableColumnState', 'is_active', $classification->getKey(), true); + + expect($classification->refresh()->is_active)->toBe(IsActive::ACTIVE); +}); + +test('unauthorized user cannot render classification page', function () { + $unauthorizedUser = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + $this->actingAs($unauthorizedUser); + + Livewire::test(ManageClassifications::class) + ->assertForbidden(); +}); + +test('user without create permission cannot see create action', function () { + $viewOnlyUser = User::factory()->create(['is_active' => IsActive::ACTIVE]); + $viewOnlyUser->givePermissionTo('ViewAny:Classification'); + $classification = Classification::factory()->create(); + + $this->actingAs($viewOnlyUser); + + Livewire::test(ManageClassifications::class) + ->assertActionHidden('create') + ->assertTableActionHidden('edit', $classification); +}); + +test('can delete classification', function () { + $classification = Classification::factory()->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->callTableAction('delete', $classification); + + $this->assertSoftDeleted($classification); +}); + +test('can restore deleted classification', function () { + // Need Developer role to see trashed records via filter + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + $classification = Classification::factory()->create(['deleted_at' => now()]); + + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->filterTable('trashed', 'with') + ->callTableAction('restore', $classification); + + $this->assertDatabaseHas('classifications', [ + 'id' => $classification->id, + 'deleted_at' => null, + ]); +}); + +test('can force delete classification', function () { + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + $classification = Classification::factory()->create(['deleted_at' => now()]); + + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->filterTable('trashed', 'only') + ->callTableAction('forceDelete', $classification); + + $this->assertDatabaseMissing('classifications', ['id' => $classification->id]); +}); + +test('trashed filter visibility depends on developer role', function () { + // Administrator but not Developer + $roleAdmin = Role::firstOrCreate(['name' => RoleEnum::ADMINISTRATOR->value, 'guard_name' => 'web']); + $this->user->assignRole($roleAdmin); + + $this->actingAs($this->user); + + Livewire::test(ManageClassifications::class) + ->assertTableFilterHidden('trashed'); + + // Upgrade to Developer + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + Livewire::test(ManageClassifications::class) + ->assertTableFilterVisible('trashed'); +}); diff --git a/tests/Feature/Filament/Resources/Master/SubClassifications/SubClassificationResourceTest.php b/tests/Feature/Filament/Resources/Master/SubClassifications/SubClassificationResourceTest.php new file mode 100644 index 0000000..9e1c7ad --- /dev/null +++ b/tests/Feature/Filament/Resources/Master/SubClassifications/SubClassificationResourceTest.php @@ -0,0 +1,200 @@ +user = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + // Setup Permissions + Permission::firstOrCreate(['name' => 'ViewAny:SubClassification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Create:SubClassification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Update:SubClassification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Delete:SubClassification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'Restore:SubClassification', 'guard_name' => 'web']); + Permission::firstOrCreate(['name' => 'ForceDelete:SubClassification', 'guard_name' => 'web']); + + $this->user->givePermissionTo([ + 'ViewAny:SubClassification', + 'Create:SubClassification', + 'Update:SubClassification', + 'Delete:SubClassification', + 'Restore:SubClassification', + 'ForceDelete:SubClassification', + ]); +}); + +test('can render sub-classification list page', function () { + $this->actingAs($this->user); + + Livewire::test(ManageSubClassifications::class) + ->assertSuccessful(); +}); + +test('can list sub-classifications', function () { + $subClassifications = SubClassification::factory()->count(5)->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageSubClassifications::class) + ->call('loadTable') + ->assertCanSeeTableRecords($subClassifications); +}); + +test('can create sub-classification', function () { + $classification = Classification::factory()->create(['is_active' => IsActive::ACTIVE]); + + $this->actingAs($this->user); + + Livewire::test(ManageSubClassifications::class) + ->mountAction('create') + ->setActionData([ + 'classification_id' => $classification->id, + 'name' => 'Sub Klasifikasi Baru', + ]) + ->callMountedAction() + ->assertHasNoActionErrors(); + + $this->assertDatabaseHas('sub_classifications', [ + 'classification_id' => $classification->id, + 'name' => 'Sub Klasifikasi Baru', + ]); +}); + +test('cannot create sub-classification with invalid data', function () { + $this->actingAs($this->user); + + // Empty name + Livewire::test(ManageSubClassifications::class) + ->mountAction('create') + ->setActionData(['name' => '', 'classification_id' => null]) + ->callMountedAction() + ->assertHasActionErrors(['name' => 'required', 'classification_id' => 'required']); + + // Non-existent classification + Livewire::test(ManageSubClassifications::class) + ->mountAction('create') + ->setActionData(['name' => 'Valid Name', 'classification_id' => 999999]) + ->callMountedAction() + ->assertHasActionErrors(['classification_id']); +}); + +test('can edit sub-classification', function () { + $subClassification = SubClassification::factory()->create(); + $newClassification = Classification::factory()->create(['is_active' => IsActive::ACTIVE]); + + $this->actingAs($this->user); + + Livewire::test(ManageSubClassifications::class) + ->mountTableAction('edit', $subClassification) + ->setActionData([ + 'classification_id' => $newClassification->id, + 'name' => 'Sub Klasifikasi Updated', + ]) + ->callMountedTableAction() + ->assertHasNoActionErrors(); + + expect($subClassification->refresh()->name)->toBe('Sub Klasifikasi Updated'); + expect($subClassification->classification_id)->toBe($newClassification->id); +}); + +test('can toggle is_active status', function () { + $subClassification = SubClassification::factory()->create(['is_active' => IsActive::INACTIVE]); + + $this->actingAs($this->user); + + Livewire::test(ManageSubClassifications::class) + ->call('updateTableColumnState', 'is_active', $subClassification->getKey(), true); + + expect($subClassification->refresh()->is_active)->toBe(IsActive::ACTIVE); +}); + +test('unauthorized user cannot render sub-classification page', function () { + $unauthorizedUser = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + $this->actingAs($unauthorizedUser); + + Livewire::test(ManageSubClassifications::class) + ->assertForbidden(); +}); + +test('user without update permission cannot see edit action', function () { + $viewOnlyUser = User::factory()->create(['is_active' => IsActive::ACTIVE]); + $viewOnlyUser->givePermissionTo('ViewAny:SubClassification'); + $subClassification = SubClassification::factory()->create(); + + $this->actingAs($viewOnlyUser); + + Livewire::test(ManageSubClassifications::class) + ->assertTableActionHidden('edit', $subClassification); +}); + +test('can delete sub-classification', function () { + $subClassification = SubClassification::factory()->create(); + + $this->actingAs($this->user); + + Livewire::test(ManageSubClassifications::class) + ->callTableAction('delete', $subClassification); + + $this->assertSoftDeleted($subClassification); +}); + +test('can restore deleted sub-classification', function () { + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + $subClassification = SubClassification::factory()->create(['deleted_at' => now()]); + + $this->actingAs($this->user); + + Livewire::test(ManageSubClassifications::class) + ->filterTable('trashed', 'with') + ->callTableAction('restore', $subClassification); + + $this->assertDatabaseHas('sub_classifications', [ + 'id' => $subClassification->id, + 'deleted_at' => null, + ]); +}); + +test('can force delete sub-classification', function () { + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + $subClassification = SubClassification::factory()->create(['deleted_at' => now()]); + + $this->actingAs($this->user); + + Livewire::test(ManageSubClassifications::class) + ->filterTable('trashed', 'only') + ->callTableAction('forceDelete', $subClassification); + + $this->assertDatabaseMissing('sub_classifications', ['id' => $subClassification->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(ManageSubClassifications::class) + ->assertTableFilterHidden('trashed'); + + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']); + $this->user->assignRole($roleDev); + + Livewire::test(ManageSubClassifications::class) + ->assertTableFilterVisible('trashed'); +});