parfum/tests/Feature/Livewire/Studio/Catalog/Bottle/CreateTest.php
Yoga Pangestu 81a9753620 feat(perfume. bottle): implementasi testing
- kasih return type
- ubah array to collection di audit
- mengubah waktu refresh tabel menjadi 30 detik
2025-12-11 11:15:59 +07:00

347 lines
12 KiB
PHP

<?php
use App\Models\Bottle;
use App\Models\Employee;
use App\Models\Outlet;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Gate;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->seed(RolePermissionSeeder::class);
$this->user = User::factory()
->active()
->has(Employee::factory())
->create();
$this->ownerRole = Role::where('name', 'Owner')->first();
$this->user->roles()->attach($this->ownerRole->id);
// Create test outlets
$this->outlets = Outlet::factory()->count(3)->create();
});
function mountBottleCreateComponent(User $user)
{
return Livewire::actingAs($user)->test(\App\Livewire\Studio\Catalog\Bottle\Create::class);
}
/*
|--------------------------------------------------------------------------
| Create Component Rendering & Mount
|--------------------------------------------------------------------------
*/
it('renders bottle create page successfully', function () {
mountBottleCreateComponent($this->user)
->assertViewIs('livewire.studio.catalog.bottle.form')
->assertViewHas('pageTitle', 'Tambah Botol');
});
it('initializes bottle form properties on mount', function () {
$component = mountBottleCreateComponent($this->user);
expect($component->form->name)->toBe('');
expect($component->form->size)->toBe('');
expect($component->form->cost_price)->toBe('');
expect($component->form->sale_price)->toBe('');
expect($component->form->description)->toBeNull();
expect($component->form->outlet_ids)->toBe([]);
expect($component->form->image)->toBe([]);
expect($component->outlets)->toHaveCount(3);
});
/*
|--------------------------------------------------------------------------
| Create Bottle Tests
|--------------------------------------------------------------------------
*/
it('creates bottle successfully when authorized', function () {
$data = [
'name' => 'New Bottle',
'size' => '100',
'cost_price' => '50000',
'sale_price' => '75000',
'description' => 'Bottle description',
'outlet_ids' => [$this->outlets->first()->id, $this->outlets->last()->id],
];
mountBottleCreateComponent($this->user)
->set('form.name', $data['name'])
->set('form.size', $data['size'])
->set('form.cost_price', $data['cost_price'])
->set('form.sale_price', $data['sale_price'])
->set('form.description', $data['description'])
->set('form.outlet_ids', $data['outlet_ids'])
->call('save')
->assertHasNoErrors()
->assertRedirect(route('studio.catalog.bottle.index'));
expect(Bottle::count())->toBe(1);
$bottle = Bottle::first();
expect($bottle->name)->toBe($data['name']);
expect($bottle->size)->toBe(100);
expect($bottle->cost_price)->toBe(50000);
expect($bottle->sale_price)->toBe(75000);
expect($bottle->point_per_pcs)->toBe(750); // 75000 / 100
expect($bottle->description)->toBe($data['description']);
expect($bottle->outlets)->toHaveCount(2);
});
it('prevents bottle creation when unauthorized', function () {
$this->ownerRole->revokePermissionTo('create bottle');
Gate::define('create bottle', fn () => false);
mountBottleCreateComponent($this->user)
->set('form.name', 'Test Bottle')
->set('form.size', '50')
->set('form.sale_price', '25000')
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertForbidden();
});
it('creates bottle with empty cost_price for non-owner roles', function () {
// Create user with limited role
$this->user->roles()->detach();
$limitedRole = Role::create(['name' => 'Cashier']);
$this->user->roles()->attach($limitedRole->id);
$createPermission = Permission::where('name', 'create bottle')->first();
$limitedRole->givePermissionTo($createPermission);
mountBottleCreateComponent($this->user)
->set('form.name', 'Test Bottle')
->set('form.size', '30')
->set('form.cost_price', '') // Empty cost price
->set('form.sale_price', '20000')
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertHasNoErrors();
$bottle = Bottle::first();
expect($bottle->cost_price)->toBe(0);
});
/*
|--------------------------------------------------------------------------
| Form Validation Tests
|--------------------------------------------------------------------------
*/
it('validates required name field on bottle create', function () {
mountBottleCreateComponent($this->user)
->set('form.name', '')
->set('form.size', '100')
->set('form.sale_price', '50000')
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertHasErrors(['form.name']);
});
it('validates name field max length on bottle create', function () {
mountBottleCreateComponent($this->user)
->set('form.name', str_repeat('a', 51))
->set('form.size', '100')
->set('form.sale_price', '50000')
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertHasErrors(['form.name']);
});
it('validates required size field on bottle create', function () {
mountBottleCreateComponent($this->user)
->set('form.name', 'Test Bottle')
->set('form.size', '')
->set('form.sale_price', '50000')
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertHasErrors(['form.size']);
});
it('validates size is unsigned integer on bottle create', function () {
mountBottleCreateComponent($this->user)
->set('form.name', 'Test Bottle')
->set('form.size', '-50') // Negative value
->set('form.sale_price', '50000')
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertHasErrors(['form.size']);
});
it('validates required sale_price field on bottle create', function () {
mountBottleCreateComponent($this->user)
->set('form.name', 'Test Bottle')
->set('form.size', '100')
->set('form.sale_price', '')
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertHasErrors(['form.sale_price']);
});
it('validates sale_price is unsigned integer on bottle create', function () {
mountBottleCreateComponent($this->user)
->set('form.name', 'Test Bottle')
->set('form.size', '100')
->set('form.sale_price', '-50000') // Negative value
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertHasErrors(['form.sale_price']);
});
it('validates required outlet_ids field on bottle create', function () {
mountBottleCreateComponent($this->user)
->set('form.name', 'Test Bottle')
->set('form.size', '100')
->set('form.sale_price', '50000')
->set('form.outlet_ids', [])
->call('save')
->assertHasErrors(['form.outlet_ids']);
});
it('validates outlet_ids contains valid outlet ids on bottle create', function () {
mountBottleCreateComponent($this->user)
->set('form.name', 'Test Bottle')
->set('form.size', '100')
->set('form.sale_price', '50000')
->set('form.outlet_ids', [999])
->call('save')
->assertHasErrors(['form.outlet_ids.*']);
});
/*
|--------------------------------------------------------------------------
| Real-time Validation Tests
|--------------------------------------------------------------------------
*/
it('validates bottle name field in real-time', function () {
$component = mountBottleCreateComponent($this->user);
// Valid name
$component->set('form.name', 'Valid Bottle Name')
->assertHasNoErrors(['form.name']);
// Empty name
$component->set('form.name', '')
->assertHasErrors(['form.name']);
// Name too long
$component->set('form.name', str_repeat('a', 51))
->assertHasErrors(['form.name']);
});
it('validates bottle size field in real-time', function () {
$component = mountBottleCreateComponent($this->user);
// Valid size
$component->set('form.size', '100')
->assertHasNoErrors(['form.size']);
// Empty size
$component->set('form.size', '')
->assertHasErrors(['form.size']);
// Negative size
$component->set('form.size', '-50')
->assertHasErrors(['form.size']);
});
it('validates bottle price fields in real-time', function () {
$component = mountBottleCreateComponent($this->user);
// Valid sale price
$component->set('form.sale_price', '50000')
->assertHasNoErrors(['form.sale_price']);
// Empty sale price
$component->set('form.sale_price', '')
->assertHasErrors(['form.sale_price']);
// Negative sale price
$component->set('form.sale_price', '-50000')
->assertHasErrors(['form.sale_price']);
// Valid cost price
$component->set('form.cost_price', '30000')
->assertHasNoErrors(['form.cost_price']);
// Negative cost price
$component->set('form.cost_price', '-30000')
->assertHasErrors(['form.cost_price']);
});
it('validates bottle outlet_ids in real-time', function () {
$component = mountBottleCreateComponent($this->user);
// Valid outlet IDs
$component->set('form.name', 'Test Bottle')
->set('form.size', '100')
->set('form.sale_price', '50000')
->set('form.outlet_ids', [$this->outlets->first()->id])
->assertHasNoErrors(['form.outlet_ids']);
// Empty outlet IDs - should trigger validation on submit
$component->set('form.outlet_ids', [])
->call('save')
->assertHasErrors(['form.outlet_ids']);
// Invalid outlet ID
$component = mountBottleCreateComponent($this->user);
$component->set('form.name', 'Test Bottle')
->set('form.size', '100')
->set('form.sale_price', '50000')
->set('form.outlet_ids', [999])
->call('save')
->assertHasErrors(['form.outlet_ids.*']);
});
/*
|--------------------------------------------------------------------------
| Integration Tests
|--------------------------------------------------------------------------
*/
it('creates bottle and attaches to outlets correctly', function () {
$outlet1 = $this->outlets->first();
$outlet2 = $this->outlets->last();
mountBottleCreateComponent($this->user)
->set('form.name', 'Integration Test Bottle')
->set('form.size', '200')
->set('form.cost_price', '75000') // Required for Owner role
->set('form.sale_price', '100000')
->set('form.outlet_ids', [$outlet1->id, $outlet2->id])
->call('save')
->assertHasNoErrors();
$bottle = Bottle::where('size', 200)->first();
expect($bottle)->not->toBeNull();
expect($bottle->outlets)->toHaveCount(2);
expect($bottle->outlets->pluck('id')->toArray())->toContain($outlet1->id, $outlet2->id);
});
it('handles large numbers in bottle price fields', function () {
mountBottleCreateComponent($this->user)
->set('form.name', 'Expensive Bottle')
->set('form.size', '500')
->set('form.cost_price', '5000000') // 5 million
->set('form.sale_price', '7500000') // 7.5 million
->set('form.outlet_ids', [$this->outlets->first()->id])
->call('save')
->assertHasNoErrors();
$bottle = Bottle::where('size', 500)->first();
expect($bottle->cost_price)->toBe(5000000);
expect($bottle->sale_price)->toBe(7500000);
expect($bottle->point_per_pcs)->toBe(75000); // 7500000 / 100
});