diff --git a/tests/Feature/Studio/Manage/Article/CreateTest.php b/tests/Feature/Studio/Manage/Article/CreateTest.php new file mode 100644 index 0000000..d1c5b64 --- /dev/null +++ b/tests/Feature/Studio/Manage/Article/CreateTest.php @@ -0,0 +1,65 @@ +setupUser(); + + // Create necessary permissions + $permissions = [ + 'view article', + 'create article', + ]; + + foreach ($permissions as $permission) { + Permission::firstOrCreate(['name' => $permission]); + } + + $role = Role::create(['name' => 'Writer']); + $role->givePermissionTo($permissions); + $this->user->assignRole($role); +}); + +it('renders the article create page', function () { + Livewire::actingAs($this->user) + ->test(Create::class) + ->assertStatus(200); +}); + +it('can store a new article', function () { + Storage::fake('public'); + $category = Category::factory()->create(['type' => CategoryType::ARTICLE]); + $image = UploadedFile::fake()->image('thumbnail.jpg'); + + Livewire::actingAs($this->user) + ->test(Create::class) + ->set('form.title', 'Test Article') + ->set('form.excerpt', 'This is a test excerpt') + ->set('form.content', 'This is the content of the test article.') + ->set('form.status', ArticleStatus::PUBLISHED->value) + ->set('form.category_ids', [$category->id]) + ->set('form.thumbnail', [$image]) + ->call('save') + ->assertRedirect(route('studio.manage.article.index', ['notification' => 'Artikel berhasil ditambahkan.'])); + + $this->assertDatabaseHas('articles', [ + 'title' => 'Test Article', + 'excerpt' => 'This is a test excerpt', + 'status' => ArticleStatus::PUBLISHED->value, + ]); +}); + +it('validates article input', function () { + Livewire::actingAs($this->user) + ->test(Create::class) + ->call('save') + ->assertHasErrors(['form.title', 'form.content', 'form.thumbnail', 'form.category_ids']); +}); diff --git a/tests/Feature/Studio/Manage/Article/IndexTest.php b/tests/Feature/Studio/Manage/Article/IndexTest.php new file mode 100644 index 0000000..4ec63c7 --- /dev/null +++ b/tests/Feature/Studio/Manage/Article/IndexTest.php @@ -0,0 +1,43 @@ +setupUser(); + + // Create necessary permissions + $permissions = [ + 'view article', + 'delete article', + ]; + + foreach ($permissions as $permission) { + Permission::firstOrCreate(['name' => $permission]); + } + + $role = Role::create(['name' => 'Writer']); + $role->givePermissionTo($permissions); + $this->user->assignRole($role); +}); + +it('renders the article index page', function () { + Livewire::actingAs($this->user) + ->test(Index::class) + ->assertStatus(200); +}); + +it('can delete an article', function () { + $article = Article::factory()->create(['author_id' => $this->user->id]); + + Livewire::actingAs($this->user) + ->test(Index::class) + ->call('delete', $article->id); + + $this->assertSoftDeleted('articles', ['id' => $article->id]); +}); diff --git a/tests/Feature/Studio/Manage/OrderTest.php b/tests/Feature/Studio/Manage/Order/CreateTest.php similarity index 86% rename from tests/Feature/Studio/Manage/OrderTest.php rename to tests/Feature/Studio/Manage/Order/CreateTest.php index c9d3e48..88a5760 100644 --- a/tests/Feature/Studio/Manage/OrderTest.php +++ b/tests/Feature/Studio/Manage/Order/CreateTest.php @@ -4,7 +4,6 @@ use App\Enums\PaymentStatus; use App\Enums\VoucherType; use App\Livewire\Studio\Manage\Order\Create; -use App\Livewire\Studio\Manage\Order\Index; use App\Models\Bottle; use App\Models\Formula; use App\Models\Order; @@ -22,7 +21,7 @@ $this->setupUser(); $role = Role::firstOrCreate(['name' => 'Owner']); - $permissions = ['view order', 'create order', 'delete order']; + $permissions = ['view order', 'create order']; foreach ($permissions as $p) { Permission::firstOrCreate(['name' => $p]); } @@ -39,13 +38,6 @@ Role::firstOrCreate(['name' => 'Leader']); }); -it('renders the order index page', function () { - $this->actingAs($this->user) - ->get(route('studio.manage.order.index')) - ->assertOk() - ->assertSeeLivewire(Index::class); -}); - it('renders the order create page', function () { $this->actingAs($this->user) ->get(route('studio.manage.order.create')) @@ -287,38 +279,6 @@ ]); }); -it('can delete an order and restore outlet stock', function () { - $product = Product::factory()->create(); - $order = Order::factory()->create([ - 'outlet_id' => $this->outlet->id, - 'user_id' => $this->user->id, - 'total' => 100000, - ]); - - $item = OrderItem::factory()->forProduct($product)->create([ - 'order_id' => $order->id, - 'user_id' => $this->user->id, - 'quantity' => 2, - ]); - - // Initial stock (after order was supposedly created) - $this->outlet->products()->attach($product->id, ['stock' => 10]); - - Livewire::actingAs($this->user) - ->test(Index::class) - ->call('delete', $order->id) - ->assertHasNoErrors(); - - $this->assertSoftDeleted('orders', ['id' => $order->id]); - - // Check stock restored (10 + 2 = 12) - $this->assertDatabaseHas('outlet_product', [ - 'outlet_id' => $this->outlet->id, - 'product_id' => $product->id, - 'stock' => 12, - ]); -}); - it('cannot create order with empty cart', function () { Livewire::actingAs($this->user) ->test(Create::class) @@ -340,14 +300,10 @@ ->assertHasErrors(['form.outlet_id' => 'required']); }); -it('cannot access order without permission', function () { +it('cannot access order create without permission', function () { $this->user->roles()->detach(); $this->user->permissions()->detach(); - $this->actingAs($this->user) - ->get(route('studio.manage.order.index')) - ->assertForbidden(); - $this->actingAs($this->user) ->get(route('studio.manage.order.create')) ->assertForbidden(); diff --git a/tests/Feature/Studio/Manage/Order/IndexTest.php b/tests/Feature/Studio/Manage/Order/IndexTest.php new file mode 100644 index 0000000..d83160f --- /dev/null +++ b/tests/Feature/Studio/Manage/Order/IndexTest.php @@ -0,0 +1,72 @@ +setupUser(); + + $role = Role::firstOrCreate(['name' => 'Owner']); + $permissions = ['view order', 'delete order']; + foreach ($permissions as $p) { + Permission::firstOrCreate(['name' => $p]); + } + $role->syncPermissions($permissions); + $this->user->assignRole($role); + + // Ensure user has an outlet + $this->user->outlets()->sync([$this->outlet->id]); +}); + +it('renders the order index page', function () { + $this->actingAs($this->user) + ->get(route('studio.manage.order.index')) + ->assertOk() + ->assertSeeLivewire(Index::class); +}); + +it('can delete an order and restore outlet stock', function () { + $product = Product::factory()->create(); + $order = Order::factory()->create([ + 'outlet_id' => $this->outlet->id, + 'user_id' => $this->user->id, + 'total' => 100000, + ]); + + $item = OrderItem::factory()->forProduct($product)->create([ + 'order_id' => $order->id, + 'user_id' => $this->user->id, + 'quantity' => 2, + ]); + + // Initial stock (after order was supposedly created) + $this->outlet->products()->attach($product->id, ['stock' => 10]); + + Livewire::actingAs($this->user) + ->test(Index::class) + ->call('delete', $order->id) + ->assertHasNoErrors(); + + $this->assertSoftDeleted('orders', ['id' => $order->id]); + + // Check stock restored (10 + 2 = 12) + $this->assertDatabaseHas('outlet_product', [ + 'outlet_id' => $this->outlet->id, + 'product_id' => $product->id, + 'stock' => 12, + ]); +}); + +it('cannot access order index without permission', function () { + $this->user->roles()->detach(); + $this->user->permissions()->detach(); + + $this->actingAs($this->user) + ->get(route('studio.manage.order.index')) + ->assertForbidden(); +}); diff --git a/tests/Feature/Studio/Manage/RestockTest.php b/tests/Feature/Studio/Manage/Restock/CreateTest.php similarity index 78% rename from tests/Feature/Studio/Manage/RestockTest.php rename to tests/Feature/Studio/Manage/Restock/CreateTest.php index 283d355..14273e7 100644 --- a/tests/Feature/Studio/Manage/RestockTest.php +++ b/tests/Feature/Studio/Manage/Restock/CreateTest.php @@ -1,11 +1,9 @@ user->assignRole($role); }); -it('renders the restock index page', function () { - Livewire::actingAs($this->user) - ->test(Index::class) - ->assertStatus(200); -}); - it('renders the restock create page', function () { Livewire::actingAs($this->user) ->test(Create::class) @@ -135,39 +125,6 @@ ]); }); -it('can delete a restock and reverse stock transfer', function () { - $warehouse = Warehouse::factory()->create(); - $perfume = Perfume::factory()->create(); - - // Initial state: Warehouse 90, Outlet 10 (simulating after a restock of 10) - $warehouse->perfumes()->attach($perfume->id, ['stock' => 90]); - $this->outlet->perfumes()->attach($perfume->id, ['stock' => 10]); - - $restock = Restock::create([ - 'warehouse_id' => $warehouse->id, - 'outlet_id' => $this->outlet->id, - 'restock_date' => now(), - 'note' => 'To be deleted', - ]); - - $restock->items()->create([ - 'user_id' => $this->user->id, - 'restockable_type' => Perfume::class, - 'restockable_id' => $perfume->id, - 'quantity' => 10, - ]); - - Livewire::actingAs($this->user) - ->test(Index::class) - ->call('delete', $restock->id); - - $this->assertSoftDeleted('restocks', ['id' => $restock->id]); - - // Verify Stock Reversal - $this->assertEquals(100, $warehouse->perfumes()->find($perfume->id)->pivot->stock); // 90 + 10 - $this->assertEquals(0, $this->outlet->perfumes()->find($perfume->id)->pivot->stock); // 10 - 10 -}); - it('validates restock input', function () { $warehouse = Warehouse::factory()->create(); $perfume = Perfume::factory()->create(); diff --git a/tests/Feature/Studio/Manage/Restock/IndexTest.php b/tests/Feature/Studio/Manage/Restock/IndexTest.php new file mode 100644 index 0000000..3fb4f50 --- /dev/null +++ b/tests/Feature/Studio/Manage/Restock/IndexTest.php @@ -0,0 +1,67 @@ +setupUser(); + $this->outlet = $this->user->outlets->first(); + + // Create permissions + $permissions = [ + 'view restock', + 'delete restock', + ]; + + foreach ($permissions as $permission) { + Permission::firstOrCreate(['name' => $permission]); + } + + $role = Role::create(['name' => 'Owner']); + $role->givePermissionTo($permissions); + $this->user->assignRole($role); +}); + +it('renders the restock index page', function () { + Livewire::actingAs($this->user) + ->test(Index::class) + ->assertStatus(200); +}); + +it('can delete a restock and reverse stock transfer', function () { + $warehouse = Warehouse::factory()->create(); + $perfume = Perfume::factory()->create(); + + // Initial state: Warehouse 90, Outlet 10 (simulating after a restock of 10) + $warehouse->perfumes()->attach($perfume->id, ['stock' => 90]); + $this->outlet->perfumes()->attach($perfume->id, ['stock' => 10]); + + $restock = Restock::create([ + 'warehouse_id' => $warehouse->id, + 'outlet_id' => $this->outlet->id, + 'restock_date' => now(), + 'note' => 'To be deleted', + ]); + + $restock->items()->create([ + 'user_id' => $this->user->id, + 'restockable_type' => Perfume::class, + 'restockable_id' => $perfume->id, + 'quantity' => 10, + ]); + + Livewire::actingAs($this->user) + ->test(Index::class) + ->call('delete', $restock->id); + + $this->assertSoftDeleted('restocks', ['id' => $restock->id]); + + // Verify Stock Reversal + $this->assertEquals(100, $warehouse->perfumes()->find($perfume->id)->pivot->stock); // 90 + 10 + $this->assertEquals(0, $this->outlet->perfumes()->find($perfume->id)->pivot->stock); // 10 - 10 +}); diff --git a/tests/Feature/Studio/Manage/StockOpnameTest.php b/tests/Feature/Studio/Manage/StockOpname/IndexTest.php similarity index 59% rename from tests/Feature/Studio/Manage/StockOpnameTest.php rename to tests/Feature/Studio/Manage/StockOpname/IndexTest.php index f85555c..148594e 100644 --- a/tests/Feature/Studio/Manage/StockOpnameTest.php +++ b/tests/Feature/Studio/Manage/StockOpname/IndexTest.php @@ -2,7 +2,6 @@ use App\Enums\StockOpnameStatus; use App\Livewire\Studio\Manage\StockOpname\Index; -use App\Livewire\Studio\Manage\StockOpname\Manage; use App\Models\Perfume; use App\Models\StockOpname; use App\Models\StockOpnameItem; @@ -18,9 +17,8 @@ $permissions = [ 'view stock opname', 'create stock opname', - 'manage stock opname', 'show stock opname', - 'delete stock opname', + 'manage stock opname', // Used in Manage but Index might need it for buttons ]; foreach ($permissions as $permission) { @@ -38,37 +36,6 @@ ->assertStatus(200); }); -it('can update physical quantity in manage page', function () { - // Create pre-existing StockOpname - $stockOpname = StockOpname::create([ - 'outlet_id' => $this->outlet->id, - 'period_month' => now()->format('Y-m'), - 'status' => StockOpnameStatus::PROCESS, - ]); - - $perfume = Perfume::factory()->create(); - $item = StockOpnameItem::create([ - 'stock_opname_id' => $stockOpname->id, - 'user_id' => $this->user->id, - 'itemable_type' => Perfume::class, - 'itemable_id' => $perfume->id, - 'qty_system' => 10, - ]); - - Livewire::actingAs($this->user) - ->test(Manage::class, ['stockOpname' => $stockOpname]) - ->set('form.outlet_stock.'.$item->id, 8) // Physical count is 8 - ->assertSet('form.outlet_stock.'.$item->id, 8); - - // Manage component updates via updated hook, or explicitly called? - // Manage.php: updated(...) calls form->update(). - - $this->assertDatabaseHas('stock_opname_items', [ - 'id' => $item->id, - 'qty_physical' => 8, - ]); -}); - it('can request approval for stock opname', function () { $stockOpname = StockOpname::create([ 'outlet_id' => $this->outlet->id, @@ -79,7 +46,7 @@ Livewire::actingAs($this->user) ->test(Index::class) - ->call('requestApproval', $stockOpname->id); // Passing ID or Model? Method signature says Model. Livewire handles ID binding. + ->call('requestApproval', $stockOpname->id); $this->assertDatabaseHas('stock_opname', [ 'id' => $stockOpname->id, @@ -95,7 +62,6 @@ ]); $perfume = Perfume::factory()->create(); - // Verify initial stock of perfume in outlet (empty or 0) // Attach to outlet first to mimic existing stock $this->outlet->perfumes()->attach($perfume->id, ['stock' => 10]); @@ -120,15 +86,3 @@ // Verify outlet stock updated $this->assertEquals(8, $this->outlet->perfumes()->find($perfume->id)->pivot->stock); }); - -it('cannot manage stock opname if not in process status', function () { - $stockOpname = StockOpname::create([ - 'outlet_id' => $this->outlet->id, - 'period_month' => now()->format('Y-m'), - 'status' => StockOpnameStatus::COMPLETED, - ]); - - Livewire::actingAs($this->user) - ->test(Manage::class, ['stockOpname' => $stockOpname]) - ->assertForbidden(); -}); diff --git a/tests/Feature/Studio/Manage/StockOpname/ManageTest.php b/tests/Feature/Studio/Manage/StockOpname/ManageTest.php new file mode 100644 index 0000000..d30e956 --- /dev/null +++ b/tests/Feature/Studio/Manage/StockOpname/ManageTest.php @@ -0,0 +1,69 @@ +setupUser(); + $this->outlet = $this->user->outlets->first(); + + // Create permissions + $permissions = [ + 'view stock opname', + 'manage stock opname', + ]; + + foreach ($permissions as $permission) { + Permission::firstOrCreate(['name' => $permission]); + } + + $role = Role::create(['name' => 'Owner']); + $role->givePermissionTo($permissions); + $this->user->assignRole($role); +}); + +it('can update physical quantity in manage page', function () { + // Create pre-existing StockOpname + $stockOpname = StockOpname::create([ + 'outlet_id' => $this->outlet->id, + 'period_month' => now()->format('Y-m'), + 'status' => StockOpnameStatus::PROCESS, + ]); + + $perfume = Perfume::factory()->create(); + $item = StockOpnameItem::create([ + 'stock_opname_id' => $stockOpname->id, + 'user_id' => $this->user->id, + 'itemable_type' => Perfume::class, + 'itemable_id' => $perfume->id, + 'qty_system' => 10, + ]); + + Livewire::actingAs($this->user) + ->test(Manage::class, ['stockOpname' => $stockOpname]) + ->set('form.outlet_stock.' . $item->id, 8) // Physical count is 8 + ->assertSet('form.outlet_stock.' . $item->id, 8); + + $this->assertDatabaseHas('stock_opname_items', [ + 'id' => $item->id, + 'qty_physical' => 8, + ]); +}); + +it('cannot manage stock opname if not in process status', function () { + $stockOpname = StockOpname::create([ + 'outlet_id' => $this->outlet->id, + 'period_month' => now()->format('Y-m'), + 'status' => StockOpnameStatus::COMPLETED, + ]); + + Livewire::actingAs($this->user) + ->test(Manage::class, ['stockOpname' => $stockOpname]) + ->assertForbidden(); +});