parfum/tests/Feature/Livewire/Studio/Manage/Article/IndexTest.php
Yoga Pangestu 4d3cef520b feat(article): implementasi testing
- kasih return type
- kasih otorisasi untuk delete
-  type di otorisasi table
2025-12-10 13:48:41 +07:00

246 lines
7.2 KiB
PHP

<?php
require_once __DIR__.'/TestHelpers.php';
use App\Enums\ArticleStatus;
use App\Livewire\Studio\Manage\Article\Index;
use App\Models\Article;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Spatie\Permission\Models\Role;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->seed(RolePermissionSeeder::class);
$this->user = User::factory()
->active()
->has(\App\Models\Employee::factory())
->create();
$this->ownerRole = Role::where('name', 'Owner')->first();
$this->user->roles()->attach($this->ownerRole->id);
$this->article = Article::factory()->create([
'author_id' => $this->user->id,
'status' => ArticleStatus::PUBLISHED,
]);
});
function mountIndexComponent(User $user): \Livewire\Features\SupportTesting\Testable
{
return Livewire::actingAs($user)->test(Index::class);
}
/*
|--------------------------------------------------------------------------
| Component Rendering & Mount
|--------------------------------------------------------------------------
*/
it('renders page successfully', function () {
mountIndexComponent($this->user)
->assertViewIs('livewire.studio.manage.article.index')
->assertViewHas('pageTitle', 'Artikel');
});
it('mounts successfully', function () {
$component = mountIndexComponent($this->user);
expect($component->instance())->toBeInstanceOf(Index::class);
});
/*
|--------------------------------------------------------------------------
| Component Properties
|--------------------------------------------------------------------------
*/
it('has title attribute set', function () {
$reflection = new ReflectionClass(Index::class);
$attributes = $reflection->getAttributes();
$titleAttribute = collect($attributes)->first(function ($attribute) {
return $attribute->getName() === 'Livewire\\Attributes\\Title';
});
expect($titleAttribute)->not->toBeNull();
expect($titleAttribute->getArguments()[0])->toBe('Artikel');
});
/*
|--------------------------------------------------------------------------
| Delete Functionality
|--------------------------------------------------------------------------
*/
it('deletes article successfully', function () {
$articleToDelete = Article::factory()->create([
'author_id' => $this->user->id,
'status' => ArticleStatus::DRAFT,
]);
mountIndexComponent($this->user)
->call('delete', $articleToDelete)
->assertDispatched('refreshDatatable');
// Verify article is soft deleted
expect($articleToDelete->fresh()->trashed())->toBeTrue();
});
it('soft deletes article', function () {
$articleToDelete = Article::factory()->create([
'author_id' => $this->user->id,
'status' => ArticleStatus::DRAFT,
]);
mountIndexComponent($this->user)
->call('delete', $articleToDelete);
// Check if article exists with trashed
$deletedArticle = Article::withTrashed()->find($articleToDelete->id);
expect($deletedArticle)->not->toBeNull();
expect($deletedArticle->trashed())->toBeTrue();
});
/*
|--------------------------------------------------------------------------
| Delete Authorization
|--------------------------------------------------------------------------
*/
it('allows deletion with delete permission', function () {
$articleToDelete = Article::factory()->create([
'author_id' => $this->user->id,
'status' => ArticleStatus::DRAFT,
]);
mountIndexComponent($this->user)
->call('delete', $articleToDelete);
expect(Article::withTrashed()->find($articleToDelete->id)->trashed())->toBeTrue();
});
it('handles deletion of non-existent article gracefully', function () {
$nonExistentArticle = new Article;
$nonExistentArticle->id = 99999;
mountIndexComponent($this->user)
->call('delete', $nonExistentArticle)
->assertDispatched('refreshDatatable');
});
/*
|--------------------------------------------------------------------------
| Component Events
|--------------------------------------------------------------------------
*/
it('dispatches refreshDatatable event on delete', function () {
$articleToDelete = Article::factory()->create([
'author_id' => $this->user->id,
'status' => ArticleStatus::DRAFT,
]);
mountIndexComponent($this->user)
->call('delete', $articleToDelete)
->assertDispatched('refreshDatatable');
});
/*
|--------------------------------------------------------------------------
| Toast Messages
|--------------------------------------------------------------------------
*/
// Toast messages are tested functionally through the delete method
/*
|--------------------------------------------------------------------------
| Flux Modal Handling
|--------------------------------------------------------------------------
*/
it('closes flux modal after deletion', function () {
$articleToDelete = Article::factory()->create([
'author_id' => $this->user->id,
'status' => ArticleStatus::DRAFT,
]);
mountIndexComponent($this->user)
->call('delete', $articleToDelete);
// This would typically test Flux modal closure
// For now, we verify the method completes
expect(true)->toBeTrue();
});
/*
|--------------------------------------------------------------------------
| Traits Usage
|--------------------------------------------------------------------------
*/
it('uses WithConfirmation trait', function () {
$component = new Index;
expect(in_array('App\Traits\WithConfirmation', class_uses_recursive($component)))->toBeTrue();
});
it('uses WithSubscribeNotification trait', function () {
$component = new Index;
expect(in_array('App\Traits\Notification\WithSubscribeNotification', class_uses_recursive($component)))->toBeTrue();
});
it('uses WithToast trait', function () {
$component = new Index;
expect(in_array('App\Traits\WithToast', class_uses_recursive($component)))->toBeTrue();
});
/*
|--------------------------------------------------------------------------
| Render Method
|--------------------------------------------------------------------------
*/
it('has render method', function () {
$component = new Index;
expect(method_exists($component, 'render'))->toBeTrue();
});
it('render method returns correct view', function () {
$component = new Index;
$view = $component->render();
expect($view->getName())->toBe('livewire.studio.manage.article.index');
expect($view->getData()['pageTitle'])->toBe('Artikel');
});
/*
|--------------------------------------------------------------------------
| Component Structure
|--------------------------------------------------------------------------
*/
it('has delete method', function () {
$component = new Index;
expect(method_exists($component, 'delete'))->toBeTrue();
});
it('delete method requires article parameter', function () {
$component = new Index;
$reflection = new ReflectionMethod($component, 'delete');
$parameters = $reflection->getParameters();
expect(count($parameters))->toBe(1);
expect($parameters[0]->getType()->getName())->toBe(Article::class);
});