refactor: restructure feature tests for StockOpname, Restock, Order, and Article into granular files.

This commit is contained in:
Yoga Pangestu 2026-01-02 22:44:28 +07:00
parent b47a7ff0a3
commit 660e772f1c
8 changed files with 320 additions and 137 deletions

View File

@ -0,0 +1,65 @@
<?php
use App\Enums\ArticleStatus;
use App\Enums\CategoryType;
use App\Livewire\Studio\Manage\Article\Create;
use App\Models\Category;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->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']);
});

View File

@ -0,0 +1,43 @@
<?php
use App\Enums\CategoryType;
use App\Livewire\Studio\Manage\Article\Index;
use App\Models\Article;
use App\Models\Category;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->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]);
});

View File

@ -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();

View File

@ -0,0 +1,72 @@
<?php
use App\Livewire\Studio\Manage\Order\Index;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->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();
});

View File

@ -1,11 +1,9 @@
<?php
use App\Livewire\Studio\Manage\Restock\Create;
use App\Livewire\Studio\Manage\Restock\Index;
use App\Models\Bottle;
use App\Models\Perfume;
use App\Models\Product;
use App\Models\Restock;
use App\Models\Warehouse;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
@ -19,8 +17,6 @@
$permissions = [
'view restock',
'create restock',
'update restock',
'delete restock',
];
foreach ($permissions as $permission) {
@ -32,12 +28,6 @@
$this->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();

View File

@ -0,0 +1,67 @@
<?php
use App\Livewire\Studio\Manage\Restock\Index;
use App\Models\Perfume;
use App\Models\Restock;
use App\Models\Warehouse;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->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
});

View File

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

View File

@ -0,0 +1,69 @@
<?php
use App\Enums\StockOpnameStatus;
use App\Livewire\Studio\Manage\StockOpname\Manage;
use App\Models\Perfume;
use App\Models\StockOpname;
use App\Models\StockOpnameItem;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
beforeEach(function () {
$this->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();
});