diff --git a/tests/Feature/Studio/Finance/ExpenseTest.php b/tests/Feature/Studio/Finance/ExpenseTest.php new file mode 100644 index 0000000..42a5ca0 --- /dev/null +++ b/tests/Feature/Studio/Finance/ExpenseTest.php @@ -0,0 +1,166 @@ +setupUser(); + + $role = Role::firstOrCreate(['name' => 'Owner']); + Permission::firstOrCreate(['name' => 'view expense']); + Permission::firstOrCreate(['name' => 'create expense']); + Permission::firstOrCreate(['name' => 'update expense']); + Permission::firstOrCreate(['name' => 'delete expense']); + $role->givePermissionTo(['view expense', 'create expense', 'update expense', 'delete expense']); + $this->user->assignRole($role); +}); + +it('renders the expense page correctly', function () { + $this->actingAs($this->user) + ->get(route('studio.finance.expense.index')) + ->assertOk() + ->assertSeeLivewire(Expense::class); +}); + +it('can open create modal', function () { + Livewire::actingAs($this->user) + ->test(Expense::class) + ->call('openModal', 'create', 'Tambah Pengeluaran') + ->assertSet('method', 'create') + ->assertSet('modalTitle', 'Tambah Pengeluaran'); +}); + +it('shows validation errors on create', function () { + Livewire::actingAs($this->user) + ->test(Expense::class) + ->call('create') + ->assertHasErrors([ + 'form.description' => 'required', + 'form.amount' => 'required', + ]); +}); + +it('can store a new expense with single outlet', function () { + // User has 1 outlet from setupUser() + expect($this->user->outlets()->count())->toBe(1); + + Livewire::actingAs($this->user) + ->test(Expense::class) + ->set('form.description', 'Beli Sabun') + ->set('form.amount', '15000') + ->call('create') + ->assertHasNoErrors() + ->assertDispatched('refreshDatatable'); + + $this->assertDatabaseHas('expenses', [ + 'description' => 'Beli Sabun', + 'amount' => 15000, + 'outlet_id' => $this->outlet->id, + 'user_id' => $this->user->id, + 'type' => ExpenseType::OPERATIONAL->value, + ]); +}); + +it('requires outlet_id if user has multiple outlets', function () { + $anotherOutlet = Outlet::factory()->create(); + $this->user->outlets()->attach($anotherOutlet); + + Livewire::actingAs($this->user) + ->test(Expense::class) + ->set('form.description', 'Beli Tissue') + ->set('form.amount', '10000') + // form.outlet_id is empty + ->call('create') + ->assertHasErrors(['form.outlet_id' => 'required']); +}); + +it('can store a new expense with multiple outlets', function () { + $anotherOutlet = Outlet::factory()->create(); + $this->user->outlets()->attach($anotherOutlet); + + Livewire::actingAs($this->user) + ->test(Expense::class) + ->set('form.description', 'Beli Tissue') + ->set('form.amount', '10000') + ->set('form.outlet_id', $anotherOutlet->id) + ->call('create') + ->assertHasNoErrors() + ->assertDispatched('refreshDatatable'); + + $this->assertDatabaseHas('expenses', [ + 'description' => 'Beli Tissue', + 'amount' => 10000, + 'outlet_id' => $anotherOutlet->id, + ]); +}); + +it('can open edit modal and load data', function () { + $expense = ExpenseModel::factory()->operational()->create([ + 'description' => 'Old Description', + 'amount' => 50000, + 'outlet_id' => $this->outlet->id, + 'user_id' => $this->user->id, + ]); + + Livewire::actingAs($this->user) + ->test(Expense::class) + ->call('openModal', 'update', 'Ubah Pengeluaran', $expense->hash_id) + ->set('form.expense', $expense) + ->set('form.description', 'Old Description') + ->set('form.amount', '50.000') + ->assertSet('method', 'update') + ->assertSet('form.description', 'Old Description') + ->assertSet('form.amount', '50.000'); +}); + +it('can update an expense', function () { + $expense = ExpenseModel::factory()->operational()->create([ + 'outlet_id' => $this->outlet->id, + 'user_id' => $this->user->id, + ]); + + Livewire::actingAs($this->user) + ->test(Expense::class) + ->call('openModal', 'update', 'Ubah Pengeluaran', $expense->hash_id) + ->set('form.expense', $expense) + ->set('form.description', 'Updated Description') + ->set('form.amount', '75000') + ->call('update') + ->assertHasNoErrors() + ->assertDispatched('refreshDatatable'); + + $this->assertDatabaseHas('expenses', [ + 'id' => $expense->id, + 'description' => 'Updated Description', + 'amount' => 75000, + ]); +}); + +it('can delete an expense', function () { + $expense = ExpenseModel::factory()->operational()->create([ + 'outlet_id' => $this->outlet->id, + 'user_id' => $this->user->id, + ]); + + Livewire::actingAs($this->user) + ->test(Expense::class) + ->call('delete', $expense->id) + ->assertHasNoErrors() + ->assertDispatched('refreshDatatable'); + + $this->assertSoftDeleted('expenses', ['id' => $expense->id]); +}); + +it('cannot access expense page without permission', function () { + $this->user->roles()->detach(); + $this->user->permissions()->detach(); + + $this->actingAs($this->user) + ->get(route('studio.finance.expense.index')) + ->assertForbidden(); +});