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