parfum/tests/Feature/Livewire/Studio/Master/FormulaTest.php

931 lines
27 KiB
PHP

<?php
use App\Livewire\Studio\Master\Formula;
use App\Models\Bottle;
use App\Models\Employee;
use App\Models\Formula as FormulaModel;
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\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);
});
function mountFormulaComponent(User $user)
{
return Livewire::actingAs($user)->test(Formula::class);
}
/*
|--------------------------------------------------------------------------
| Component Rendering & Mount
|--------------------------------------------------------------------------
*/
it('renders page successfully', function () {
mountFormulaComponent($this->user)
->assertViewIs('livewire.studio.master.formulas')
->assertViewHas('pageTitle', 'Rumus');
});
it('mounts formulas correctly grouped by size', function () {
$bottle1 = Bottle::factory()->create(['size' => 100]);
$bottle2 = Bottle::factory()->create(['size' => 200]);
$formula1 = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$formula2 = FormulaModel::factory()->create([
'quality' => 'Super Premium',
'size' => 100,
'volume' => 30,
]);
$formula3 = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 200,
'volume' => 40,
]);
$component = mountFormulaComponent($this->user);
expect($component->formulas)->toBeInstanceOf(\Illuminate\Support\Collection::class);
expect($component->formulas)->toHaveKey('100');
expect($component->formulas)->toHaveKey('200');
expect($component->formulas[100])->toBeInstanceOf(\Illuminate\Support\Collection::class);
expect($component->formulas[100]->count())->toBe(2);
expect($component->formulas[200]->count())->toBe(1);
});
it('mounts formulas sorted by size', function () {
$bottle1 = Bottle::factory()->create(['size' => 200]);
$bottle2 = Bottle::factory()->create(['size' => 100]);
$bottle3 = Bottle::factory()->create(['size' => 300]);
FormulaModel::factory()->create(['size' => 200]);
FormulaModel::factory()->create(['size' => 100]);
FormulaModel::factory()->create(['size' => 300]);
$component = mountFormulaComponent($this->user);
$sizes = $component->formulas->keys()->toArray();
expect($sizes[0])->toBeLessThan($sizes[1]);
expect($sizes[1])->toBeLessThan($sizes[2]);
});
it('mounts formulas sorted by volume within each size group', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula1 = FormulaModel::factory()->create([
'size' => 100,
'volume' => 30,
]);
$formula2 = FormulaModel::factory()->create([
'size' => 100,
'volume' => 10,
]);
$formula3 = FormulaModel::factory()->create([
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user);
$volumes = $component->formulas[100]->pluck('volume')->toArray();
expect($volumes[0])->toBeLessThan($volumes[1]);
expect($volumes[1])->toBeLessThan($volumes[2]);
});
it('mounts sizes from bottles correctly', function () {
$bottle1 = Bottle::factory()->create(['size' => 100]);
$bottle2 = Bottle::factory()->create(['size' => 200]);
$bottle3 = Bottle::factory()->create(['size' => 300]);
$component = mountFormulaComponent($this->user);
expect($component->sizes)->toBeArray();
expect($component->sizes)->toContain(100, 200, 300);
});
it('mounts sizes sorted correctly', function () {
$bottle1 = Bottle::factory()->create(['size' => 300]);
$bottle2 = Bottle::factory()->create(['size' => 100]);
$bottle3 = Bottle::factory()->create(['size' => 200]);
$component = mountFormulaComponent($this->user);
expect($component->sizes[0])->toBeLessThan($component->sizes[1]);
expect($component->sizes[1])->toBeLessThan($component->sizes[2]);
});
it('mounts qualities correctly', function () {
$component = mountFormulaComponent($this->user);
expect($component->qualities)->toBeArray();
expect($component->qualities)->toContain('Premium', 'Super Premium', 'Murni');
});
it('displays empty state when no formulas exist', function () {
$component = mountFormulaComponent($this->user);
expect($component->formulas)->toBeInstanceOf(\Illuminate\Support\Collection::class);
expect($component->formulas)->toBeEmpty();
});
it('mounts with correct initial method and modal title', function () {
$component = mountFormulaComponent($this->user);
expect($component->method)->toBe('create');
expect($component->modalTitle)->toBe('');
});
/*
|--------------------------------------------------------------------------
| Modal Operations
|--------------------------------------------------------------------------
*/
it('opens modal for create', function () {
$component = mountFormulaComponent($this->user);
$component->dispatch(
'modal:open',
'create',
'Tambah Rumus'
);
expect($component->method)->toBe('create');
expect($component->modalTitle)->toBe('Tambah Rumus');
});
it('opens modal for update with formula data', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user);
$component->dispatch(
'modal:open',
'update',
'Ubah Rumus',
$formula->hash
);
expect($component->method)->toBe('update');
expect($component->modalTitle)->toBe('Ubah Rumus');
expect($component->form->quality)->toBe('Premium');
expect($component->form->size)->toBe('100');
expect($component->form->volume)->toBe('20');
});
it('resets validation when opening modal', function () {
$component = mountFormulaComponent($this->user);
$component->set('form.quality', '')
->call('create')
->assertHasErrors('form.quality');
$component->dispatch(
'modal:open',
'create',
'Tambah Rumus'
);
$component->assertHasNoErrors();
});
it('resets error bag when opening modal', function () {
$component = mountFormulaComponent($this->user);
$component->set('form.quality', '')
->call('create')
->assertHasErrors();
$component->dispatch(
'modal:open',
'create',
'Tambah Rumus'
);
expect($component->get('form.quality'))->toBe('');
});
/*
|--------------------------------------------------------------------------
| Create Formula
|--------------------------------------------------------------------------
*/
it('prevents create when user is unauthorized', function () {
$this->ownerRole->revokePermissionTo('create formula');
Gate::define('create formula', fn () => false);
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '20')
->call('create')
->assertForbidden();
});
it('creates formula successfully when authorized', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$component = mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '20')
->call('create')
->assertHasNoErrors();
$this->assertDatabaseHas('formulas', [
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$formula = FormulaModel::where('quality', 'Premium')
->where('size', 100)
->first();
expect($formula)->not->toBeNull();
expect($formula->volume)->toBe(20);
});
it('adds new formula to formulas array after creation', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$component = mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '20')
->call('create');
expect($component->formulas)->toHaveKey('100');
expect($component->formulas[100])->toBeArray();
$found = false;
foreach ($component->formulas[100] as $item) {
if ($item['quality'] === 'Premium' && $item['volume'] === 20) {
$found = true;
expect($item)->toHaveKey('hash');
break;
}
}
expect($found)->toBeTrue();
});
it('prevents creating duplicate formula with same quality and size', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '30')
->call('create');
expect(FormulaModel::where('quality', 'Premium')->where('size', 100)->count())->toBe(1);
});
it('shows warning when creating duplicate formula', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '30')
->call('create')
->assertHasNoErrors();
});
it('allows creating formula with different quality but same size', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->set('form.quality', 'Super Premium')
->set('form.size', '100')
->set('form.volume', '30')
->call('create')
->assertHasNoErrors();
expect(FormulaModel::where('size', 100)->count())->toBe(2);
});
it('allows creating formula with same quality but different size', function () {
$bottle1 = Bottle::factory()->create(['size' => 100]);
$bottle2 = Bottle::factory()->create(['size' => 200]);
FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '200')
->set('form.volume', '40')
->call('create')
->assertHasNoErrors();
expect(FormulaModel::where('quality', 'Premium')->count())->toBe(2);
});
it('creates formula with all quality types', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$qualities = ['Premium', 'Super Premium', 'Murni'];
foreach ($qualities as $quality) {
mountFormulaComponent($this->user)
->set('form.quality', $quality)
->set('form.size', '100')
->set('form.volume', '20')
->call('create')
->assertHasNoErrors();
}
expect(FormulaModel::where('size', 100)->count())->toBe(3);
});
/*
|--------------------------------------------------------------------------
| Update Formula
|--------------------------------------------------------------------------
*/
it('prevents update when user is unauthorized', function () {
$this->ownerRole->revokePermissionTo('update formula');
Gate::define('update formula', fn () => false);
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula->hash)
->set('form.volume', '30')
->call('update')
->assertForbidden();
});
it('updates formula successfully when authorized', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula->hash)
->set('form.volume', '30')
->call('update')
->assertHasNoErrors();
$formula->refresh();
expect($formula->volume)->toBe(30);
$this->assertDatabaseHas('formulas', [
'id' => $formula->id,
'volume' => 30,
]);
});
it('updates formula quality', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula->hash)
->set('form.quality', 'Super Premium')
->set('form.volume', '25')
->call('update')
->assertHasNoErrors();
$formula->refresh();
expect($formula->quality)->toBe('Super Premium');
expect($formula->volume)->toBe(25);
});
it('updates formula size', function () {
$bottle1 = Bottle::factory()->create(['size' => 100]);
$bottle2 = Bottle::factory()->create(['size' => 200]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula->hash)
->set('form.size', '200')
->set('form.volume', '40')
->call('update')
->assertHasNoErrors();
$formula->refresh();
expect($formula->size)->toBe(200);
expect($formula->volume)->toBe(40);
});
it('updates formulas array after update', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula->hash)
->set('form.volume', '30')
->call('update');
$found = false;
foreach ($component->formulas[100] as $item) {
if ($item['hash'] === $formula->hash && $item['volume'] === 30) {
$found = true;
break;
}
}
expect($found)->toBeTrue();
});
it('prevents updating to duplicate quality and size combination', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula1 = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$formula2 = FormulaModel::factory()->create([
'quality' => 'Super Premium',
'size' => 100,
'volume' => 30,
]);
$component = mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula2->hash)
->set('form.quality', 'Premium')
->set('form.size', '100')
->call('update');
$formula2->refresh();
expect($formula2->quality)->toBe('Super Premium');
});
it('allows updating to same quality and size if it is the same formula', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula->hash)
->set('form.volume', '25')
->call('update')
->assertHasNoErrors();
$formula->refresh();
expect($formula->volume)->toBe(25);
expect($formula->quality)->toBe('Premium');
expect($formula->size)->toBe(100);
});
it('updates formula with formatted volume input', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula->hash)
->set('form.volume', '1.500')
->call('update')
->assertHasNoErrors();
$formula->refresh();
expect($formula->volume)->toBe(1500);
});
/*
|--------------------------------------------------------------------------
| Delete Formula
|--------------------------------------------------------------------------
*/
it('deletes formula successfully when authorized', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
mountFormulaComponent($this->user)
->call('delete', $formula)
->assertHasNoErrors();
$this->assertSoftDeleted('formulas', ['id' => $formula->id]);
});
it('removes formula from formulas array after deletion', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user);
$initialCount = count($component->formulas[100] ?? []);
$component->call('delete', $formula);
if (isset($component->formulas[100])) {
expect(count($component->formulas[100]))->toBeLessThan($initialCount);
} else {
expect($component->formulas)->not->toHaveKey('100');
}
});
it('removes size group when last formula in group is deleted', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user);
expect($component->formulas)->toHaveKey('100');
$component->call('delete', $formula);
expect($component->formulas)->not->toHaveKey('100');
});
it('keeps other formulas in group when one is deleted', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula1 = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$formula2 = FormulaModel::factory()->create([
'quality' => 'Super Premium',
'size' => 100,
'volume' => 30,
]);
$component = mountFormulaComponent($this->user);
expect(count($component->formulas[100]))->toBe(2);
$component->call('delete', $formula1);
expect($component->formulas)->toHaveKey('100');
expect(count($component->formulas[100]))->toBe(1);
$remaining = $component->formulas[100][0];
expect($remaining['quality'])->toBe('Super Premium');
});
/*
|--------------------------------------------------------------------------
| Validation Rules
|--------------------------------------------------------------------------
*/
it('validates quality field is required', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.size', '100')
->set('form.volume', '20')
->call('create')
->assertHasErrors(['form.quality']);
});
it('validates quality field max length', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', str_repeat('a', 21))
->set('form.size', '100')
->set('form.volume', '20')
->call('create')
->assertHasErrors(['form.quality']);
});
it('validates size field is required', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.volume', '20')
->call('create')
->assertHasErrors(['form.size']);
});
it('validates size field is unsigned integer', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '-100')
->set('form.volume', '20')
->call('create')
->assertHasErrors(['form.size']);
});
it('validates volume field is required', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->call('create')
->assertHasErrors(['form.volume']);
});
it('validates volume field is unsigned integer', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '-20')
->call('create')
->assertHasErrors(['form.volume']);
});
it('validates volume field is valid integer', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '150')
->call('create')
->assertHasNoErrors();
});
it('validates volume field can be equal to size', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '100')
->call('create')
->assertHasNoErrors();
$this->assertDatabaseHas('formulas', [
'size' => 100,
'volume' => 100,
]);
});
it('validates volume field accepts formatted numbers', function () {
$bottle = Bottle::factory()->create(['size' => 1000]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '1000')
->set('form.volume', '5.000')
->call('create')
->assertHasNoErrors();
$this->assertDatabaseHas('formulas', [
'size' => 1000,
'volume' => 5000,
]);
});
/*
|--------------------------------------------------------------------------
| Edge Cases & Additional Scenarios
|--------------------------------------------------------------------------
*/
it('handles multiple formulas with same size correctly', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$qualities = ['Premium', 'Super Premium', 'Murni'];
foreach ($qualities as $quality) {
mountFormulaComponent($this->user)
->set('form.quality', $quality)
->set('form.size', '100')
->set('form.volume', '20')
->call('create')
->assertHasNoErrors();
}
$component = mountFormulaComponent($this->user);
expect($component->formulas)->toHaveKey('100');
expect(count($component->formulas[100]))->toBe(3);
});
it('handles formulas with different sizes correctly', function () {
$bottle1 = Bottle::factory()->create(['size' => 100]);
$bottle2 = Bottle::factory()->create(['size' => 200]);
$bottle3 = Bottle::factory()->create(['size' => 300]);
$sizes = [100, 200, 300];
foreach ($sizes as $size) {
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', (string) $size)
->set('form.volume', '20')
->call('create')
->assertHasNoErrors();
}
$component = mountFormulaComponent($this->user);
expect($component->formulas)->toHaveKey('100');
expect($component->formulas)->toHaveKey('200');
expect($component->formulas)->toHaveKey('300');
});
it('handles empty formulas array after all deletions', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user);
$component->call('delete', $formula);
expect($component->formulas)->toBeInstanceOf(\Illuminate\Support\Collection::class);
expect($component->formulas)->not->toHaveKey('100');
});
it('maintains correct order after update', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula1 = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 10,
]);
$formula2 = FormulaModel::factory()->create([
'quality' => 'Super Premium',
'size' => 100,
'volume' => 30,
]);
$component = mountFormulaComponent($this->user)
->call('openModal', 'update', 'Ubah Rumus', $formula1->hash)
->set('form.volume', '50')
->call('update');
$volumes = array_column($component->formulas[100], 'volume');
expect($volumes[0])->toBeLessThan($volumes[1]);
});
it('handles very large volume values', function () {
$bottle = Bottle::factory()->create(['size' => 10000]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '10000')
->set('form.volume', '9999')
->call('create')
->assertHasNoErrors();
$this->assertDatabaseHas('formulas', [
'size' => 10000,
'volume' => 9999,
]);
});
it('handles minimum volume value', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '1')
->call('create')
->assertHasNoErrors();
$this->assertDatabaseHas('formulas', [
'size' => 100,
'volume' => 1,
]);
});
it('handles zero volume value', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
mountFormulaComponent($this->user)
->set('form.quality', 'Premium')
->set('form.size', '100')
->set('form.volume', '0')
->call('create')
->assertHasNoErrors();
$this->assertDatabaseHas('formulas', [
'size' => 100,
'volume' => 0,
]);
});
it('handles confirmation action for delete', function () {
$bottle = Bottle::factory()->create(['size' => 100]);
$formula = FormulaModel::factory()->create([
'quality' => 'Premium',
'size' => 100,
'volume' => 20,
]);
$component = mountFormulaComponent($this->user);
$component->dispatch('fn:confirmAction', $formula->hash);
expect($component->confirmingId)->toBe($formula->hash);
});