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); });