diff --git a/docs/testing-guidlines.md b/docs/testing-guidlines.md new file mode 100644 index 0000000..97baedd --- /dev/null +++ b/docs/testing-guidlines.md @@ -0,0 +1,187 @@ +# Panduan Pengujian (Testing Guidelines) Filament Resource + +Dokumen ini berisi standar dan pola pengujian untuk semua Filament Resource dalam aplikasi ini menggunakan **Pest** dan **Livewire Test Utility**. + +--- + +## 1. Persiapan (Setup) + +Setiap file test harus menggunakan trait `RefreshDatabase` dan menyiapkan environment di dalam blok `beforeEach`. + +### Komponen Wajib Setup: +- **User**: Buat user aktif menggunakan factory. +- **Permissions**: Daftarkan permission yang relevan dengan Resource (format: `Action:Model`). +- **Role**: Assign permission ke user tersebut. + +```php +uses(RefreshDatabase::class); + +beforeEach(function () { + $this->user = User::factory()->create(['is_active' => IsActive::ACTIVE]); + + // Contoh untuk Resource 'Post' + $permissions = [ + 'ViewAny:Post', + 'Create:Post', + 'Update:Post', + 'Delete:Post', + 'Restore:Post', + 'ForceDelete:Post', + ]; + + foreach ($permissions as $permission) { + Permission::firstOrCreate(['name' => $permission, 'guard_name' => 'web']); + } + + $this->user->givePermissionTo($permissions); +}); +``` + +--- + +## 2. Kategori Pengujian + +### A. Rendering & Otorisasi +Memastikan halaman dapat diakses oleh user yang berhak dan ditolak untuk yang tidak berhak. + +- **Positif**: `assertSuccessful()`. +- **Negatif**: `assertForbidden()`. + +```php +test('halaman list dapat diakses', function () { + $this->actingAs($this->user); + Livewire::test(ManagePosts::class)->assertSuccessful(); +}); + +test('user tanpa akses ditolak mengakses halaman', function () { + $guest = User::factory()->create(); + $this->actingAs($guest); + Livewire::test(ManagePosts::class)->assertForbidden(); +}); +``` + +### B. Menampilkan Data (Listing) +Memastikan tabel menampilkan data yang sesuai dan filter berfungsi (jika ada). + +```php +test('dapat menampilkan daftar data', function () { + $posts = Post::factory()->count(3)->create(); + $this->actingAs($this->user); + + Livewire::test(ManagePosts::class) + ->call('loadTable') // Jika menggunakan deferLoading + ->assertCanSeeTableRecords($posts); +}); +``` + +### C. Operasi CRUD (Create, Read, Update, Delete) +Gunakan `mountAction` untuk action di level halaman (seperti 'create') dan `mountTableAction` untuk action di baris tabel (seperti 'edit'). + +```php +// Create +test('dapat membuat data baru', function () { + $this->actingAs($this->user); + Livewire::test(ManagePosts::class) + ->mountAction('create') + ->setActionData(['title' => 'Judul Baru']) + ->callMountedAction() + ->assertHasNoActionErrors(); + + $this->assertDatabaseHas('posts', ['title' => 'Judul Baru']); +}); + +// Edit +test('dapat mengubah data', function () { + $post = Post::factory()->create(); + $this->actingAs($this->user); + + Livewire::test(ManagePosts::class) + ->mountTableAction('edit', $post) + ->setActionData(['title' => 'Judul Update']) + ->callMountedTableAction() + ->assertHasNoActionErrors(); + + expect($post->refresh()->title)->toBe('Judul Update'); +}); +``` + +### D. Validasi Form +Uji setiap aturan validasi kritis (required, unique, max, dll). + +```php +test('validasi: judul wajib diisi', function () { + $this->actingAs($this->user); + Livewire::test(ManagePosts::class) + ->mountAction('create') + ->setActionData(['title' => '']) + ->callMountedAction() + ->assertHasActionErrors(['title' => 'required']); +}); +``` + +### E. Soft Delete & Trash +Pengujian untuk fitur restore dan force delete biasanya membutuhkan role **Developer**. + +```php +test('dapat menghapus data (soft delete)', function () { + $post = Post::factory()->create(); + $this->actingAs($this->user); + + Livewire::test(ManagePosts::class)->callTableAction('delete', $post); + $this->assertSoftDeleted($post); +}); + +test('dapat merestore data yang dihapus', function () { + // Berikan role Developer agar bisa melihat filter 'trashed' + $roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value]); + $this->user->assignRole($roleDev); + + $post = Post::factory()->create(['deleted_at' => now()]); + $this->actingAs($this->user); + + Livewire::test(ManagePosts::class) + ->filterTable('trashed', 'with') + ->callTableAction('restore', $post); + + $this->assertDatabaseHas('posts', ['id' => $post->id, 'deleted_at' => null]); +}); +``` + +### F. Fitur Khusus: Filter & Toggle +Jangan lupa menguji kolom toggle dan filter kustom. + +```php +// Toggle Status +test('dapat mengubah status is_active via toggle', function () { + $post = Post::factory()->create(['is_active' => false]); + $this->actingAs($this->user); + + Livewire::test(ManagePosts::class) + ->call('updateTableColumnState', 'is_active', $post->id, true); + + expect($post->refresh()->is_active)->toBeTrue(); +}); + +// Filter +test('dapat memfilter data berdasarkan kategori', function () { + $postA = Post::factory()->create(['category' => 'A']); + $postB = Post::factory()->create(['category' => 'B']); + + $this->actingAs($this->user); + + Livewire::test(ManagePosts::class) + ->filterTable('category', 'A') + ->assertCanSeeTableRecords([$postA]) + ->assertCanNotSeeTableRecords([$postB]); +}); +``` + +--- + +## 3. Praktik Terbaik (Best Practices) + +1. **Granularitas**: Satu test case fokus pada satu aturan validasi atau satu alur logika. +2. **Keamanan**: Selalu sertakan test untuk user yang tidak memiliki permission (unauthorized). +3. **Clean Up**: Gunakan `RefreshDatabase` agar data antar test tidak saling mengganggu. +4. **Factory**: Pastikan factory menghasilkan data yang valid sesuai integritas database. +5. **UI State**: Gunakan `assertActionHidden()` untuk memastikan tombol (seperti 'edit' atau 'delete') benar-benar tersembunyi bagi user yang tidak punya akses.