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 data $this->outlets = Outlet::factory()->count(3)->create(); $this->brands = Brand::factory()->count(2)->create(); $this->categories = Category::factory()->count(3)->create(); }); function mountPerfumeCreateComponent(User $user) { return Livewire::actingAs($user)->test(\App\Livewire\Studio\Catalog\Perfume\Create::class); } /* |-------------------------------------------------------------------------- | Create Component Rendering & Mount |-------------------------------------------------------------------------- */ it('renders perfume create page successfully', function () { mountPerfumeCreateComponent($this->user) ->assertViewIs('livewire.studio.catalog.perfume.form') ->assertViewHas('pageTitle', 'Tambah Parfum'); }); it('initializes perfume form properties on mount', function () { $component = mountPerfumeCreateComponent($this->user); expect($component->form->name)->toBe(''); expect($component->form->sku)->toBe(''); expect($component->form->brand_id)->toBeNull(); expect($component->form->cost_price)->toBe(''); expect($component->form->sale_price)->toBe(''); expect($component->form->concentration)->toBe('2'); expect($component->form->base_notes)->toBeNull(); expect($component->form->middle_notes)->toBeNull(); expect($component->form->top_notes)->toBeNull(); expect($component->form->description)->toBeNull(); expect($component->form->category_ids)->toBe([]); expect($component->form->outlet_ids)->toBe([]); expect($component->form->image)->toBe([]); expect($component->outlets)->toHaveCount(3); expect($component->brands)->toHaveCount(2); expect($component->categories)->toHaveCount(3); }); /* |-------------------------------------------------------------------------- | Create Perfume Tests |-------------------------------------------------------------------------- */ it('creates perfume successfully when authorized', function () { $data = [ 'name' => 'New Perfume', 'sku' => 'NP001', 'brand_id' => $this->brands->first()->id, 'cost_price' => '150000', 'sale_price' => '250000', 'concentration' => Concentration::EAU_DE_PERFUME->value, 'base_notes' => 'Vanilla, Musk', 'middle_notes' => 'Jasmine, Rose', 'top_notes' => 'Bergamot, Lemon', 'description' => 'A beautiful fragrance', 'category_ids' => [$this->categories->first()->id, $this->categories->last()->id], 'outlet_ids' => [$this->outlets->first()->id, $this->outlets->last()->id], ]; mountPerfumeCreateComponent($this->user) ->set('form.name', $data['name']) ->set('form.sku', $data['sku']) ->set('form.brand_id', $data['brand_id']) ->set('form.cost_price', $data['cost_price']) ->set('form.sale_price', $data['sale_price']) ->set('form.concentration', $data['concentration']) ->set('form.base_notes', $data['base_notes']) ->set('form.middle_notes', $data['middle_notes']) ->set('form.top_notes', $data['top_notes']) ->set('form.description', $data['description']) ->set('form.category_ids', $data['category_ids']) ->set('form.outlet_ids', $data['outlet_ids']) ->call('save') ->assertHasNoErrors() ->assertRedirect(route('studio.catalog.perfume.index')); expect(Perfume::count())->toBe(1); $perfume = Perfume::first(); expect($perfume->name)->toBe($data['name']); expect($perfume->sku)->toBe($data['sku']); expect($perfume->brand_id)->toBe($data['brand_id']); expect($perfume->cost_price)->toBe(150000); expect($perfume->sale_price)->toBe(250000); expect($perfume->concentration->value)->toBe($data['concentration']); expect($perfume->base_notes)->toBe($data['base_notes']); expect($perfume->middle_notes)->toBe($data['middle_notes']); expect($perfume->top_notes)->toBe($data['top_notes']); expect($perfume->description)->toBe($data['description']); expect($perfume->categories)->toHaveCount(2); expect($perfume->outlets)->toHaveCount(2); }); it('prevents perfume creation when unauthorized', function () { $this->ownerRole->revokePermissionTo('create perfume'); Gate::define('create perfume', fn () => false); mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertForbidden(); }); it('creates perfume 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 perfume')->first(); $limitedRole->givePermissionTo($createPermission); mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.cost_price', '') // Empty cost price ->set('form.sale_price', '200000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasNoErrors(); $perfume = Perfume::first(); expect($perfume->cost_price)->toBe(0); }); it('creates perfume with optional brand', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Perfume Without Brand') ->set('form.sku', 'PWB001') ->set('form.brand_id', null) // No brand ->set('form.cost_price', '100000') // Required for Owner role ->set('form.sale_price', '150000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasNoErrors(); $perfume = Perfume::first(); expect($perfume->brand_id)->toBeNull(); }); /* |-------------------------------------------------------------------------- | Form Validation Tests |-------------------------------------------------------------------------- */ it('validates required name field on perfume create', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', '') ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.name']); }); it('validates name field max length on perfume create', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', str_repeat('a', 51)) ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.name']); }); it('validates required sku field on perfume create', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', '') ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.sku']); }); it('validates sku field max length on perfume create', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', str_repeat('a', 21)) ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.sku']); }); it('validates sku uniqueness on perfume create', function () { Perfume::factory()->create(['sku' => 'EXISTING001']); mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'EXISTING001') ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.sku']); }); it('validates brand_id exists if provided', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.brand_id', 999) // Non-existent brand ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.brand_id']); }); it('validates required sale_price field on perfume create', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.sale_price', '') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.sale_price']); }); it('validates concentration is valid enum value', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.concentration', 'invalid') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.concentration']); }); it('validates required category_ids field on perfume create', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.category_ids', []) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.category_ids']); }); it('validates category_ids contains valid category ids', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.category_ids', [999]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.category_ids.*']); }); it('validates required outlet_ids field on perfume create', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', []) ->call('save') ->assertHasErrors(['form.outlet_ids']); }); it('validates outlet_ids contains valid outlet ids on perfume create', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [999]) ->call('save') ->assertHasErrors(['form.outlet_ids.*']); }); it('validates notes fields max length', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Test Perfume') ->set('form.sku', 'TP001') ->set('form.sale_price', '100000') ->set('form.base_notes', str_repeat('a', 256)) // Exceeds 255 ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasErrors(['form.base_notes']); }); /* |-------------------------------------------------------------------------- | Real-time Validation Tests |-------------------------------------------------------------------------- */ it('validates perfume name field in real-time', function () { $component = mountPerfumeCreateComponent($this->user); // Valid name $component->set('form.name', 'Valid Perfume 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 perfume sku field in real-time', function () { $component = mountPerfumeCreateComponent($this->user); // Valid SKU $component->set('form.sku', 'VALID001') ->assertHasNoErrors(['form.sku']); // Empty SKU $component->set('form.sku', '') ->assertHasErrors(['form.sku']); // SKU too long $component->set('form.sku', str_repeat('a', 21)) ->assertHasErrors(['form.sku']); // Duplicate SKU Perfume::factory()->create(['sku' => 'EXISTING001']); $component->set('form.sku', 'EXISTING001') ->assertHasErrors(['form.sku']); }); it('validates perfume price fields in real-time', function () { $component = mountPerfumeCreateComponent($this->user); // Valid sale price $component->set('form.sale_price', '200000') ->assertHasNoErrors(['form.sale_price']); // Empty sale price $component->set('form.sale_price', '') ->assertHasErrors(['form.sale_price']); // Negative sale price $component->set('form.sale_price', '-200000') ->assertHasErrors(['form.sale_price']); }); it('validates perfume concentration field in real-time', function () { $component = mountPerfumeCreateComponent($this->user); // Valid concentration $component->set('form.concentration', Concentration::EAU_DE_PERFUME->value) ->assertHasNoErrors(['form.concentration']); // Invalid concentration $component->set('form.concentration', 'invalid') ->assertHasErrors(['form.concentration']); }); /* |-------------------------------------------------------------------------- | Integration Tests |-------------------------------------------------------------------------- */ it('creates perfume and attaches to categories and outlets correctly', function () { $category1 = $this->categories->first(); $category2 = $this->categories->last(); $outlet1 = $this->outlets->first(); $outlet2 = $this->outlets->last(); $brand = $this->brands->first(); mountPerfumeCreateComponent($this->user) ->set('form.name', 'Integration Test Perfume') ->set('form.sku', 'ITP001') ->set('form.brand_id', $brand->id) ->set('form.cost_price', '200000') // Required for Owner role ->set('form.sale_price', '350000') ->set('form.concentration', Concentration::EAU_DE_PERFUME->value) ->set('form.category_ids', [$category1->id, $category2->id]) ->set('form.outlet_ids', [$outlet1->id, $outlet2->id]) ->call('save') ->assertHasNoErrors(); $perfume = Perfume::where('sku', 'ITP001')->first(); expect($perfume)->not->toBeNull(); expect($perfume->brand->id)->toBe($brand->id); expect($perfume->categories)->toHaveCount(2); expect($perfume->outlets)->toHaveCount(2); expect($perfume->categories->pluck('id')->toArray())->toContain($category1->id, $category2->id); expect($perfume->outlets->pluck('id')->toArray())->toContain($outlet1->id, $outlet2->id); }); it('handles large numbers in perfume price fields', function () { mountPerfumeCreateComponent($this->user) ->set('form.name', 'Expensive Perfume') ->set('form.sku', 'EP001') ->set('form.cost_price', '10000000') // 10 million ->set('form.sale_price', '15000000') // 15 million ->set('form.category_ids', [$this->categories->first()->id]) ->set('form.outlet_ids', [$this->outlets->first()->id]) ->call('save') ->assertHasNoErrors(); $perfume = Perfume::where('sku', 'EP001')->first(); expect($perfume->cost_price)->toBe(10000000); expect($perfume->sale_price)->toBe(15000000); });