From 77d91b977ff49babce56d5646eb99ed2bac2932a Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Wed, 10 Dec 2025 13:26:44 +0700 Subject: [PATCH] feat(faq): implemntasi testing - kasih return type - menyesuaikan factory - mengganti modalName - menyesuaikan seeder - menyesuaikan panggil component --- .../Studio/Information/FaqsTable.php | 13 +- app/Livewire/Studio/Information/Faq.php | 41 ++-- app/Models/Faq.php | 3 +- database/factories/FaqFactory.php | 4 +- database/seeders/FaqSeeder.php | 3 +- .../studio/information/faqs.blade.php | 3 +- .../Livewire/Studio/Information/FaqTest.php | 194 ++++++++++++++++++ 7 files changed, 230 insertions(+), 31 deletions(-) create mode 100644 tests/Feature/Livewire/Studio/Information/FaqTest.php diff --git a/app/Livewire/Datatable/Studio/Information/FaqsTable.php b/app/Livewire/Datatable/Studio/Information/FaqsTable.php index 32bca99..0ff8f17 100644 --- a/app/Livewire/Datatable/Studio/Information/FaqsTable.php +++ b/app/Livewire/Datatable/Studio/Information/FaqsTable.php @@ -27,7 +27,7 @@ public function columns(): array if (auth()->user()->can('update faq')) { if ($row->sort_order > 1) { - $actions .= view('components.datatables.sort-button', [ + $actions .= view('components.actions.table.sort-button', [ 'id' => $row->hash, 'module' => 'faq', 'direction' => 'up', @@ -36,7 +36,7 @@ public function columns(): array 'icon' => 'arrow-up', ])->render(); - $actions .= view('components.datatables.sort-button', [ + $actions .= view('components.actions.table.sort-button', [ 'id' => $row->hash, 'module' => 'faq', 'direction' => 'first', @@ -47,7 +47,7 @@ public function columns(): array } if ($row->sort_order < $maxSortOrder) { - $actions .= view('components.datatables.sort-button', [ + $actions .= view('components.actions.table.sort-button', [ 'id' => $row->hash, 'module' => 'faq', 'direction' => 'down', @@ -56,7 +56,7 @@ public function columns(): array 'icon' => 'arrow-down', ])->render(); - $actions .= view('components.datatables.sort-button', [ + $actions .= view('components.actions.table.sort-button', [ 'id' => $row->hash, 'module' => 'faq', 'direction' => 'last', @@ -68,7 +68,7 @@ public function columns(): array } if (auth()->user()->can('update faq')) { - $actions .= view('components.datatables.edit-modal', [ + $actions .= view('components.actions.table.edit-modal', [ 'id' => $row->hash, 'method' => 'update', 'modalTitle' => 'Ubah FAQs', @@ -76,9 +76,8 @@ public function columns(): array } if (auth()->user()->can('delete faq')) { - $actions .= view('components.datatables.delete', [ + $actions .= view('components.actions.table.delete', [ 'id' => $row->hash, - 'deleteRoute' => route('studio.information.faq.delete', $row->hash), ])->render(); } diff --git a/app/Livewire/Studio/Information/Faq.php b/app/Livewire/Studio/Information/Faq.php index 5422167..267a09a 100644 --- a/app/Livewire/Studio/Information/Faq.php +++ b/app/Livewire/Studio/Information/Faq.php @@ -11,6 +11,7 @@ use App\Traits\WithToast; use App\Traits\WithUpdatedData; use Flux\Flux; +use Illuminate\Contracts\View\View; use Livewire\Attributes\On; use Livewire\Attributes\Title; use Livewire\Component; @@ -26,21 +27,7 @@ class Faq extends Component public string $modalTitle = ''; - #[On('modal:open')] - public function openModal(string $method, string $modalTitle, ?string $id = null) - { - $this->resetValidation(); - $this->resetErrorBag(); - - $this->method = $method; - $this->modalTitle = $modalTitle; - - if ($id) { - $this->form->setFaq(FaqModel::byHashOrFail($id)); - } - } - - public function create() + public function create(): void { $this->canOrAbort('create faq'); @@ -53,7 +40,7 @@ public function create() Flux::modals()->close(); } - public function update() + public function update(): void { $this->canOrAbort('update faq'); @@ -66,8 +53,10 @@ public function update() Flux::modals()->close(); } - public function delete(FaqModel $faq) + public function delete(FaqModel $faq): void { + $this->canOrAbort('delete faq'); + $this->form->faq = $faq; $this->form->delete(); @@ -80,7 +69,7 @@ public function delete(FaqModel $faq) } #[On('fn:sortOrder')] - public function sortOrder(FaqModel $faq, string $direction) + public function sortOrder(FaqModel $faq, string $direction): void { $this->canOrAbort('update faq'); @@ -91,7 +80,21 @@ public function sortOrder(FaqModel $faq, string $direction) $this->dispatch('refreshDatatable'); } - public function render() + #[On('modal:open')] + public function openModal(string $method, string $modalTitle, ?string $id = null): void + { + $this->resetValidation(); + $this->resetErrorBag(); + + $this->method = $method; + $this->modalTitle = $modalTitle; + + if ($id) { + $this->form->setFaq(FaqModel::byHashOrFail($id)); + } + } + + public function render(): View { return view('livewire.studio.information.faqs', [ 'pageTitle' => 'FAQs', diff --git a/app/Models/Faq.php b/app/Models/Faq.php index 0a7767a..29e8ec7 100644 --- a/app/Models/Faq.php +++ b/app/Models/Faq.php @@ -2,13 +2,14 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Veelasky\LaravelHashId\Eloquent\HashableId; class Faq extends Model { - use HashableId, SoftDeletes; + use HasFactory, HashableId, SoftDeletes; protected $guarded = ['id']; diff --git a/database/factories/FaqFactory.php b/database/factories/FaqFactory.php index a87c601..fb8575d 100644 --- a/database/factories/FaqFactory.php +++ b/database/factories/FaqFactory.php @@ -12,8 +12,8 @@ class FaqFactory extends Factory public function definition(): array { return [ - 'question' => $this->faker->sentence(6), - 'answer' => $this->faker->paragraph(), + 'question' => 'Sample FAQ question?', + 'answer' => 'This is a sample answer for the FAQ.', 'sort_order' => $this->faker->numberBetween(1, 50), ]; } diff --git a/database/seeders/FaqSeeder.php b/database/seeders/FaqSeeder.php index b4e0d7f..dc75810 100644 --- a/database/seeders/FaqSeeder.php +++ b/database/seeders/FaqSeeder.php @@ -2,13 +2,14 @@ namespace Database\Seeders; +use App\Models\Faq; use Illuminate\Database\Seeder; class FaqSeeder extends Seeder { public function run(): void { - DB::table('faqs')->insert([ + Faq::insert([ [ 'question' => 'Bagaimana cara memesan parfum di toko online ini?', 'answer' => 'Cukup pilih parfum yang kamu suka, tambahkan ke keranjang, lalu lanjutkan ke pembayaran melalui metode yang tersedia.', diff --git a/resources/views/livewire/studio/information/faqs.blade.php b/resources/views/livewire/studio/information/faqs.blade.php index 76c1712..108b988 100644 --- a/resources/views/livewire/studio/information/faqs.blade.php +++ b/resources/views/livewire/studio/information/faqs.blade.php @@ -19,13 +19,14 @@ @include('components.modals.confirmation', [ - 'modalName' => 'confirmation-modal', + 'modalName' => 'delete-confirmation', 'modalTitle' => 'Apakah Anda yakin?', 'modalMessage' => 'Data yang berelasi dengan data ini juga akan ikut terhapus.', 'buttonVariant' => 'primary', 'buttonColor' => 'danger', 'buttonText' => 'Ya, Hapus', ]) +
{{ $modalTitle }} diff --git a/tests/Feature/Livewire/Studio/Information/FaqTest.php b/tests/Feature/Livewire/Studio/Information/FaqTest.php new file mode 100644 index 0000000..4b0ab1d --- /dev/null +++ b/tests/Feature/Livewire/Studio/Information/FaqTest.php @@ -0,0 +1,194 @@ + 'create faq', 'guard_name' => 'web']); + Permission::create(['name' => 'update faq', 'guard_name' => 'web']); + Permission::create(['name' => 'delete faq', 'guard_name' => 'web']); + + // Create role and assign permissions + $role = Role::create(['name' => 'Admin', 'guard_name' => 'web']); + $role->givePermissionTo(['create faq', 'update faq', 'delete faq']); + + // Create admin user + $this->adminUser = User::factory()->create(); + $this->adminUser->assignRole('Admin'); +}); + +it('renders page successfully', function () { + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->assertOk() + ->assertSet('method', 'create') + ->assertSet('modalTitle', '') + ->assertSee('FAQs'); +}); + +it('has title attribute', function () { + $component = new Faq; + $reflection = new \ReflectionClass($component); + $attributes = $reflection->getAttributes(\Livewire\Attributes\Title::class); + + expect($attributes)->not->toBeEmpty(); +}); + +it('opens modal for create', function () { + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->call('openModal', 'create', 'Tambah FAQs') + ->assertSet('method', 'create') + ->assertSet('modalTitle', 'Tambah FAQs') + ->assertSet('form.question', '') + ->assertSet('form.answer', ''); +}); + +it('opens modal for update', function () { + $faq = FaqModel::factory()->create([ + 'question' => 'Test Question', + 'answer' => 'Test Answer', + ]); + + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->call('openModal', 'update', 'Ubah FAQs', $faq->hash) + ->assertSet('method', 'update') + ->assertSet('modalTitle', 'Ubah FAQs') + ->assertSet('form.question', 'Test Question') + ->assertSet('form.answer', 'Test Answer'); +}); + +it('creates faq successfully', function () { + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->set('form.question', 'What is Laravel?') + ->set('form.answer', 'Laravel is a PHP framework.') + ->call('create') + ->assertDispatched('refreshDatatable'); + + $this->assertDatabaseHas('faqs', [ + 'question' => 'What is Laravel?', + 'answer' => 'Laravel is a PHP framework.', + 'sort_order' => 1, + ]); +}); + +it('requires permission to create faq', function () { + $userWithoutPermission = User::factory()->create(); + + Livewire::actingAs($userWithoutPermission) + ->test(Faq::class) + ->set('form.question', 'Test Question') + ->set('form.answer', 'Test Answer') + ->call('create') + ->assertForbidden(); +}); + +it('updates faq successfully', function () { + $faq = FaqModel::factory()->create([ + 'question' => 'Old Question', + 'answer' => 'Old Answer', + ]); + + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->call('openModal', 'update', 'Ubah FAQs', $faq->hash) + ->set('form.question', 'Updated Question') + ->set('form.answer', 'Updated Answer') + ->call('update') + ->assertDispatched('refreshDatatable'); + + $this->assertDatabaseHas('faqs', [ + 'id' => $faq->id, + 'question' => 'Updated Question', + 'answer' => 'Updated Answer', + ]); +}); + +it('requires permission to update faq', function () { + $faq = FaqModel::factory()->create(); + $userWithoutPermission = User::factory()->create(); + + Livewire::actingAs($userWithoutPermission) + ->test(Faq::class) + ->call('openModal', 'update', 'Ubah FAQs', $faq->hash) + ->set('form.question', 'Updated Question') + ->set('form.answer', 'Updated Answer') + ->call('update') + ->assertForbidden(); +}); + +it('deletes faq successfully', function () { + $faq = FaqModel::factory()->create(); + + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->call('delete', $faq) + ->assertDispatched('refreshDatatable'); + + expect($faq->trashed())->toBeTrue(); +}); + +it('sorts order up', function () { + $faq1 = FaqModel::factory()->create(['sort_order' => 1]); + $faq2 = FaqModel::factory()->create(['sort_order' => 2]); + + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->call('sortOrder', $faq2, 'up') + ->assertDispatched('refreshDatatable'); + + expect($faq1->fresh()->sort_order)->toBe(2); + expect($faq2->fresh()->sort_order)->toBe(1); +}); + +it('sorts order down', function () { + $faq1 = FaqModel::factory()->create(['sort_order' => 1]); + $faq2 = FaqModel::factory()->create(['sort_order' => 2]); + + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->call('sortOrder', $faq1, 'down') + ->assertDispatched('refreshDatatable'); + + expect($faq1->fresh()->sort_order)->toBe(2); + expect($faq2->fresh()->sort_order)->toBe(1); +}); + +it('requires permission to sort order', function () { + $faq = FaqModel::factory()->create(); + $userWithoutPermission = User::factory()->create(); + + Livewire::actingAs($userWithoutPermission) + ->test(Faq::class) + ->call('sortOrder', $faq, 'up') + ->assertForbidden(); +}); + +it('displays validation errors', function () { + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->set('form.question', '') // Empty question + ->set('form.answer', '') // Empty answer + ->call('create') + ->assertHasErrors(['form.question', 'form.answer']); +}); + +it('opens modal correctly', function () { + $faq = FaqModel::factory()->create(); + + Livewire::actingAs($this->adminUser) + ->test(Faq::class) + ->call('openModal', 'create', 'Tambah FAQs') + ->assertSet('method', 'create') + ->assertSet('modalTitle', 'Tambah FAQs'); +});