# 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.
