feat: add perfume CRUD and audit feature tests and remove informational callouts from catalog index views

This commit is contained in:
Yoga Pangestu 2026-01-02 19:34:17 +07:00
parent 43b37c8c7f
commit 6a0271d9e1
7 changed files with 227 additions and 24 deletions

View File

@ -14,14 +14,6 @@ class="text-sm">
</div>
<div class="mt-6">
<flux:callout icon="megaphone" color="blue" class="mb-6">
<flux:callout.heading>Informasi</flux:callout.heading>
<flux:callout.text>
Untuk melihat harga pembelian, silakan klik pada bagian harga. Untuk melakukan audit stok,
silakan klik pada nama outlet.
</flux:callout.text>
</flux:callout>
<livewire:datatable.catalog.bottles-table />
</div>

View File

@ -21,14 +21,6 @@ class="text-sm">
</div>
<div class="mt-6">
<flux:callout icon="megaphone" color="blue" class="mb-6">
<flux:callout.heading>Informasi</flux:callout.heading>
<flux:callout.text>
Untuk melihat harga pembelian, silakan klik pada bagian harga. Untuk melakukan audit stok,
silakan klik pada nama outlet.
</flux:callout.text>
</flux:callout>
<livewire:datatable.catalog.perfumes-table />
</div>

View File

@ -21,14 +21,6 @@ class="text-sm">
</div>
<div class="mt-6">
<flux:callout icon="megaphone" color="blue" class="mb-6">
<flux:callout.heading>Informasi</flux:callout.heading>
<flux:callout.text>
Untuk melihat harga pembelian, silakan klik pada bagian harga. Untuk melakukan audit stok,
silakan klik pada nama outlet.
</flux:callout.text>
</flux:callout>
<livewire:datatable.catalog.products-table />
</div>

View File

@ -0,0 +1,38 @@
<?php
use App\Livewire\Studio\Catalog\Perfume\Audit;
use App\Models\Outlet;
use App\Models\Perfume;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->setupUser();
$role = Role::firstOrCreate(['name' => 'Owner']);
Permission::firstOrCreate(['name' => 'view perfume']);
Permission::firstOrCreate(['name' => 'audit perfume']);
$role->givePermissionTo(['view perfume', 'audit perfume']);
$this->user->assignRole($role);
});
it('renders the perfume audit page correctly', function () {
$outlet = Outlet::factory()->create();
$perfume = Perfume::factory()->create();
$this->actingAs($this->user)
->get(route('studio.catalog.perfume.audit', ['outlet' => $outlet, 'perfume' => $perfume]))
->assertOk()
->assertSeeLivewire(Audit::class);
});
it('cannot access perfume audit without permission', function () {
$outlet = Outlet::factory()->create();
$perfume = Perfume::factory()->create();
$this->user->roles()->detach();
$this->user->permissions()->detach();
$this->actingAs($this->user)
->get(route('studio.catalog.perfume.audit', ['outlet' => $outlet, 'perfume' => $perfume]))
->assertForbidden();
});

View File

@ -0,0 +1,77 @@
<?php
use App\Enums\Concentration;
use App\Livewire\Studio\Catalog\Perfume\Create;
use App\Models\Brand;
use App\Models\Category;
use App\Models\Outlet;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->setupUser();
$role = Role::firstOrCreate(['name' => 'Owner']);
Permission::firstOrCreate(['name' => 'view perfume']);
Permission::firstOrCreate(['name' => 'create perfume']);
$role->givePermissionTo(['view perfume', 'create perfume']);
$this->user->assignRole($role);
});
it('renders the perfume create page correctly', function () {
$this->actingAs($this->user)
->get(route('studio.catalog.perfume.create'))
->assertOk()
->assertSeeLivewire(Create::class);
});
it('shows validation errors when fields are empty', function () {
Livewire::actingAs($this->user)
->test(Create::class)
->call('save')
->assertHasErrors([
'form.name' => 'required',
'form.sku' => 'required',
'form.cost_price' => 'required', // Required for Owner
'form.sale_price' => 'required',
'form.category_ids' => 'required',
'form.outlet_ids' => 'required',
]);
});
it('can store a new perfume', function () {
$brand = Brand::factory()->create();
$category = Category::factory()->create();
$outlet = Outlet::factory()->create();
Livewire::actingAs($this->user)
->test(Create::class)
->set('form.name', 'Perfume A')
->set('form.sku', 'SKU-Perfume-A')
->set('form.brand_id', $brand->id)
->set('form.cost_price', '50000')
->set('form.sale_price', '100000')
->set('form.concentration', Concentration::EAU_DE_PERFUME->value)
->set('form.category_ids', [$category->id])
->set('form.outlet_ids', [$outlet->id])
->call('save')
->assertHasNoErrors()
->assertRedirect(route('studio.catalog.perfume.index', ['notification' => 'Parfum berhasil ditambahkan.']));
$this->assertDatabaseHas('perfumes', [
'name' => 'Perfume A',
'sku' => 'SKU-Perfume-A',
'cost_price' => 50000,
'sale_price' => 100000,
]);
});
it('cannot access perfume create without permission', function () {
$this->user->roles()->detach();
$this->user->permissions()->detach();
$this->actingAs($this->user)
->get(route('studio.catalog.perfume.create'))
->assertForbidden();
});

View File

@ -0,0 +1,67 @@
<?php
use App\Livewire\Studio\Catalog\Perfume\Edit;
use App\Models\Category;
use App\Models\Outlet;
use App\Models\Perfume;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->setupUser();
$role = Role::firstOrCreate(['name' => 'Owner']);
Permission::firstOrCreate(['name' => 'view perfume']);
Permission::firstOrCreate(['name' => 'update perfume']);
$role->givePermissionTo(['view perfume', 'update perfume']);
$this->user->assignRole($role);
});
it('renders the perfume edit page correctly', function () {
$perfume = Perfume::factory()->create();
$this->actingAs($this->user)
->get(route('studio.catalog.perfume.edit', ['perfume' => $perfume]))
->assertOk()
->assertSeeLivewire(Edit::class);
});
it('can load data correctly into edit form', function () {
$perfume = Perfume::factory()->create(['name' => 'Old Name']);
Livewire::actingAs($this->user)
->test(Edit::class, ['perfume' => $perfume])
->assertSet('form.name', 'Old Name')
->assertSet('form.perfume.id', $perfume->id);
});
it('can update a perfume', function () {
$perfume = Perfume::factory()->create();
$category = Category::factory()->create();
$outlet = Outlet::factory()->create();
Livewire::actingAs($this->user)
->test(Edit::class, ['perfume' => $perfume])
->set('form.name', 'Updated Name')
->set('form.category_ids', [$category->id])
->set('form.outlet_ids', [$outlet->id])
->call('save')
->assertHasNoErrors()
->assertRedirect(route('studio.catalog.perfume.index', ['notification' => 'Parfum berhasil diperbarui.']));
$this->assertDatabaseHas('perfumes', [
'id' => $perfume->id,
'name' => 'Updated Name',
]);
});
it('cannot access perfume edit without permission', function () {
$perfume = Perfume::factory()->create();
$this->user->roles()->detach();
$this->user->permissions()->detach();
$this->actingAs($this->user)
->get(route('studio.catalog.perfume.edit', ['perfume' => $perfume]))
->assertForbidden();
});

View File

@ -0,0 +1,45 @@
<?php
use App\Livewire\Studio\Catalog\Perfume\Index;
use App\Models\Perfume;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->setupUser();
$role = Role::firstOrCreate(['name' => 'Owner']);
Permission::firstOrCreate(['name' => 'view perfume']);
Permission::firstOrCreate(['name' => 'delete perfume']);
$role->givePermissionTo(['view perfume', 'delete perfume']);
$this->user->assignRole($role);
});
it('renders the perfume index page correctly', function () {
$this->actingAs($this->user)
->get(route('studio.catalog.perfume.index'))
->assertOk()
->assertSeeLivewire(Index::class);
});
it('can delete a perfume', function () {
$perfume = Perfume::factory()->create();
Livewire::actingAs($this->user)
->test(Index::class)
->call('delete', $perfume->id)
->assertHasNoErrors()
->assertDispatched('refreshDatatable');
$this->assertSoftDeleted('perfumes', ['id' => $perfume->id]);
});
it('cannot access perfume index without permission', function () {
$this->user->roles()->detach();
$this->user->permissions()->detach();
$this->actingAs($this->user)
->get(route('studio.catalog.perfume.index'))
->assertForbidden();
});