test: Add comprehensive feature tests for Department and Theme Filament resources, covering CRUD, authorization, and soft delete functionalities.
This commit is contained in:
parent
718df3e35d
commit
e1be243351
@ -0,0 +1,222 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\IsActive;
|
||||||
|
use App\Enums\RoleEnum;
|
||||||
|
use App\Filament\Resources\Master\Departments\Pages\ManageDepartments;
|
||||||
|
use App\Models\Department;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->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');
|
||||||
|
});
|
||||||
@ -0,0 +1,214 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\IsActive;
|
||||||
|
use App\Enums\RoleEnum;
|
||||||
|
use App\Filament\Resources\Master\Themes\Pages\ManageThemes;
|
||||||
|
use App\Models\Theme;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->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');
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user