- kasih otorisasi saat delete dan reset password - memperbaiki testnya - kasih kondisi untuk data kosong jangan di tampilkan di view
496 lines
17 KiB
PHP
496 lines
17 KiB
PHP
<?php
|
|
|
|
use App\Enums\VoucherType;
|
|
use App\Livewire\Studio\Loyalty\Voucher\Edit;
|
|
use App\Models\Employee;
|
|
use App\Models\Facility;
|
|
use App\Models\OpeningHour;
|
|
use App\Models\Outlet;
|
|
use App\Models\User;
|
|
use App\Models\Voucher;
|
|
use Database\Seeders\RolePermissionSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use Spatie\Permission\Models\Role;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
$this->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);
|
|
});
|
|
|
|
function mountEditComponent(User $user, Voucher $voucher)
|
|
{
|
|
return Livewire::actingAs($user)->test(Edit::class, ['voucher' => $voucher]);
|
|
}
|
|
|
|
function createTestOutlets(int $count = 3)
|
|
{
|
|
return Outlet::factory()
|
|
->count($count)
|
|
->has(OpeningHour::factory()->count(7))
|
|
->has(Facility::factory()->count(5))
|
|
->create();
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Component Rendering & Mount
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
it('renders page successfully', function () {
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach(createOutlets(2)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->assertViewIs('livewire.studio.loyalty.voucher.form')
|
|
->assertViewHas('pageTitle', 'Ubah Voucher');
|
|
});
|
|
|
|
it('loads outlets on mount', function () {
|
|
$voucher = Voucher::factory()->create();
|
|
$outlets = createOutlets(3);
|
|
|
|
$component = mountEditComponent($this->user, $voucher);
|
|
|
|
expect($component->outlets)->toHaveCount(3);
|
|
expect($component->outlets)->toHaveKey($outlets->first()->id);
|
|
});
|
|
|
|
it('loads voucher data into form on mount', function () {
|
|
$outlets = createOutlets(2);
|
|
$voucher = Voucher::factory()->create([
|
|
'name' => 'Test Voucher',
|
|
'code' => 'TEST123',
|
|
'tags' => 'Promo',
|
|
'type' => VoucherType::FIXED->value,
|
|
'discount_amount' => 25000,
|
|
'min_purchase' => 100000,
|
|
'max_discount' => null,
|
|
'quota' => '50',
|
|
'limit_per_user' => '2',
|
|
'summary' => 'Test summary',
|
|
'start_date' => now()->addDay()->format('Y-m-d'),
|
|
'end_date' => now()->addMonth()->format('Y-m-d'),
|
|
]);
|
|
$voucher->outlets()->attach($outlets->pluck('id'));
|
|
|
|
$component = mountEditComponent($this->user, $voucher);
|
|
|
|
expect($component->form->name)->toBe('Test Voucher');
|
|
expect($component->form->code)->toBe('TEST123');
|
|
expect($component->form->tags)->toBe('Promo');
|
|
expect($component->form->type)->toBe((string) VoucherType::FIXED->value);
|
|
expect($component->form->summary)->toBe('Test summary');
|
|
expect($component->form->quota)->toBe('50');
|
|
expect($component->form->limit_per_user)->toBe('2');
|
|
expect($component->form->start_date)->toBe(now()->addDay()->format('Y-m-d'));
|
|
expect($component->form->end_date)->toBe(now()->addMonth()->format('Y-m-d'));
|
|
expect($component->form->outlet_ids)->toEqual($outlets->pluck('id')->toArray());
|
|
});
|
|
|
|
it('formats currency values correctly when loading voucher', function () {
|
|
$voucher = Voucher::factory()->create([
|
|
'discount_amount' => 50000,
|
|
'min_purchase' => 200000,
|
|
'max_discount' => 100000,
|
|
]);
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
$component = mountEditComponent($this->user, $voucher);
|
|
|
|
// Note: replaceCurrency returns integer, so we check the raw value
|
|
// The form will display formatted currency in the view
|
|
expect($component->form->discount_amount)->toBe('50000');
|
|
expect($component->form->min_purchase)->toBe('200000');
|
|
expect($component->form->max_discount)->toBe('100000');
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Update Voucher - Fixed Type
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
it('can update a fixed type voucher', function () {
|
|
$outlets = createOutlets(3);
|
|
$voucher = Voucher::factory()->create([
|
|
'type' => VoucherType::FIXED->value,
|
|
]);
|
|
$voucher->outlets()->attach($outlets->take(2)->pluck('id'));
|
|
|
|
$newOutlets = createOutlets(2);
|
|
$allOutlets = $outlets->merge($newOutlets);
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.name', 'Updated Voucher')
|
|
->set('form.code', 'UPDATED')
|
|
->set('form.tags', 'Updated Tag')
|
|
->set('form.discount_amount', '30000')
|
|
->set('form.min_purchase', '150000')
|
|
->set('form.quota', '75')
|
|
->set('form.limit_per_user', '3')
|
|
->set('form.summary', 'Updated summary')
|
|
->set('form.start_date', now()->addDays(2)->format('Y-m-d'))
|
|
->set('form.end_date', now()->addMonths(2)->format('Y-m-d'))
|
|
->set('form.outlet_ids', $allOutlets->pluck('id')->toArray())
|
|
->call('save')
|
|
->assertHasNoErrors()
|
|
->assertRedirect(route('studio.loyalty.voucher.index'));
|
|
|
|
$voucher->refresh();
|
|
expect($voucher->name)->toBe('Updated Voucher');
|
|
expect($voucher->code)->toBe('UPDATED');
|
|
expect($voucher->tags)->toBe('Updated Tag');
|
|
expect($voucher->discount_amount)->toBe(30000);
|
|
expect($voucher->min_purchase)->toBe(150000);
|
|
expect($voucher->quota)->toBe(75);
|
|
expect($voucher->limit_per_user)->toBe(3);
|
|
expect($voucher->outlets)->toHaveCount(5);
|
|
});
|
|
|
|
it('can update voucher to remove optional fields', function () {
|
|
$voucher = Voucher::factory()->create([
|
|
'min_purchase' => 50000,
|
|
'quota' => 100,
|
|
'limit_per_user' => 2,
|
|
'end_date' => now()->addMonth()->format('Y-m-d'),
|
|
]);
|
|
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.min_purchase', null)
|
|
->set('form.quota', null)
|
|
->set('form.limit_per_user', null)
|
|
->set('form.end_date', null)
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$voucher->refresh();
|
|
expect($voucher->min_purchase)->toBeNull();
|
|
expect($voucher->quota)->toBeNull();
|
|
expect($voucher->limit_per_user)->toBeNull();
|
|
expect($voucher->end_date)->toBeNull();
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Update Voucher - Percentage Type
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
it('can update voucher from fixed to percentage type', function () {
|
|
$voucher = Voucher::factory()->create([
|
|
'type' => VoucherType::FIXED->value,
|
|
'discount_amount' => 10000,
|
|
'max_discount' => null,
|
|
]);
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.type', VoucherType::PERCENTAGE->value)
|
|
->set('form.discount_amount', '15')
|
|
->set('form.max_discount', '40000')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$voucher->refresh();
|
|
expect($voucher->type)->toBe(VoucherType::PERCENTAGE);
|
|
expect($voucher->discount_amount)->toBe(15);
|
|
expect($voucher->max_discount)->toBe(40000);
|
|
});
|
|
|
|
it('can update voucher from percentage to fixed type', function () {
|
|
$voucher = Voucher::factory()->create([
|
|
'type' => VoucherType::PERCENTAGE->value,
|
|
'discount_amount' => 20,
|
|
'max_discount' => 50000,
|
|
]);
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.type', VoucherType::FIXED->value)
|
|
->set('form.discount_amount', '25000')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$voucher->refresh();
|
|
expect($voucher->type)->toBe(VoucherType::FIXED);
|
|
expect($voucher->discount_amount)->toBe(25000);
|
|
expect($voucher->max_discount)->toBeNull();
|
|
});
|
|
|
|
it('can update percentage voucher with new max discount', function () {
|
|
$voucher = Voucher::factory()->create([
|
|
'type' => VoucherType::PERCENTAGE->value,
|
|
'discount_amount' => 10,
|
|
'max_discount' => 30000,
|
|
]);
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.discount_amount', '25')
|
|
->set('form.max_discount', '60000')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$voucher->refresh();
|
|
expect($voucher->discount_amount)->toBe(25);
|
|
expect($voucher->max_discount)->toBe(60000);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Outlet Management
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
it('can update voucher outlets', function () {
|
|
$originalOutlets = createOutlets(2);
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach($originalOutlets->pluck('id'));
|
|
|
|
$newOutlets = createOutlets(3);
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.outlet_ids', $newOutlets->pluck('id')->toArray())
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$voucher->refresh();
|
|
expect($voucher->outlets)->toHaveCount(3);
|
|
expect($voucher->outlets->pluck('id')->toArray())->toEqual($newOutlets->pluck('id')->toArray());
|
|
});
|
|
|
|
it('can add more outlets to existing voucher', function () {
|
|
$originalOutlets = createOutlets(2);
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach($originalOutlets->pluck('id'));
|
|
|
|
$additionalOutlets = createOutlets(2);
|
|
$allOutlets = $originalOutlets->merge($additionalOutlets);
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.outlet_ids', $allOutlets->pluck('id')->toArray())
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$voucher->refresh();
|
|
expect($voucher->outlets)->toHaveCount(4);
|
|
});
|
|
|
|
it('can remove all outlets from voucher', function () {
|
|
$outlets = createOutlets(3);
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach($outlets->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.outlet_ids', [])
|
|
->call('save')
|
|
->assertHasErrors(['form.outlet_ids']); // Should fail validation
|
|
});
|
|
|
|
it('can select all outlets when editing', function () {
|
|
$outlets = createOutlets(5);
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach($outlets->take(2)->pluck('id'));
|
|
|
|
$component = mountEditComponent($this->user, $voucher);
|
|
$component->call('selectAllOutlets');
|
|
|
|
expect($component->form->outlet_ids)->toHaveCount(5);
|
|
expect($component->form->outlet_ids)->toEqual($outlets->pluck('id')->toArray());
|
|
});
|
|
|
|
it('can deselect all outlets when editing', function () {
|
|
$outlets = createOutlets(3);
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach($outlets->pluck('id'));
|
|
|
|
$component = mountEditComponent($this->user, $voucher);
|
|
expect($component->form->outlet_ids)->toHaveCount(3);
|
|
|
|
$component->call('deselectAllOutlets');
|
|
|
|
expect($component->form->outlet_ids)->toHaveCount(0);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Validation Tests
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
it('validates required fields on update', function () {
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.name', '')
|
|
->set('form.code', '')
|
|
->set('form.tags', '')
|
|
->set('form.summary', '')
|
|
->set('form.start_date', '')
|
|
->set('form.outlet_ids', [])
|
|
->call('save')
|
|
->assertHasErrors([
|
|
'form.name',
|
|
'form.code',
|
|
'form.tags',
|
|
'form.summary',
|
|
'form.start_date',
|
|
'form.outlet_ids',
|
|
]);
|
|
});
|
|
|
|
it('allows same code when updating voucher', function () {
|
|
$voucher = Voucher::factory()->create(['code' => 'EXISTING']);
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.name', 'Updated Name')
|
|
->set('form.code', 'EXISTING') // Same code
|
|
->set('form.tags', 'Test')
|
|
->set('form.type', VoucherType::FIXED->value)
|
|
->set('form.discount_amount', '10000')
|
|
->set('form.summary', 'Test')
|
|
->set('form.start_date', now()->addDay()->format('Y-m-d'))
|
|
->set('form.outlet_ids', [$voucher->outlets->first()->id])
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
});
|
|
|
|
it('validates code uniqueness when changing to existing code', function () {
|
|
$voucher1 = Voucher::factory()->create(['code' => 'VOUCHER1']);
|
|
$voucher2 = Voucher::factory()->create(['code' => 'VOUCHER2']);
|
|
$voucher1->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher1)
|
|
->set('form.code', 'VOUCHER2') // Trying to use existing code
|
|
->set('form.name', 'Test')
|
|
->set('form.tags', 'Test')
|
|
->set('form.type', VoucherType::FIXED->value)
|
|
->set('form.discount_amount', '10000')
|
|
->set('form.summary', 'Test')
|
|
->set('form.start_date', now()->addDay()->format('Y-m-d'))
|
|
->set('form.outlet_ids', [$voucher1->outlets->first()->id])
|
|
->call('save')
|
|
->assertHasErrors(['form.code']);
|
|
});
|
|
|
|
it('validates percentage discount requires max_discount on update', function () {
|
|
$voucher = Voucher::factory()->create([
|
|
'type' => VoucherType::PERCENTAGE->value,
|
|
'max_discount' => 50000,
|
|
]);
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.max_discount', null)
|
|
->call('save')
|
|
->assertHasErrors(['form.max_discount']);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Permissions
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
it('prevents update when user lacks update voucher permission', function () {
|
|
$this->ownerRole->revokePermissionTo('update voucher');
|
|
Gate::define('update voucher', fn () => false);
|
|
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.name', 'Updated')
|
|
->set('form.code', 'UPDATED')
|
|
->set('form.tags', 'Test')
|
|
->set('form.type', VoucherType::FIXED->value)
|
|
->set('form.discount_amount', '10000')
|
|
->set('form.summary', 'Test')
|
|
->set('form.start_date', now()->addDay()->format('Y-m-d'))
|
|
->set('form.outlet_ids', [$voucher->outlets->first()->id])
|
|
->call('save')
|
|
->assertForbidden();
|
|
});
|
|
|
|
it('allows update when user has update voucher permission', function () {
|
|
$voucher = Voucher::factory()->create();
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.name', 'Updated')
|
|
->set('form.code', 'UPDATED')
|
|
->set('form.tags', 'Test')
|
|
->set('form.type', VoucherType::FIXED->value)
|
|
->set('form.discount_amount', '10000')
|
|
->set('form.summary', 'Test')
|
|
->set('form.start_date', now()->addDay()->format('Y-m-d'))
|
|
->set('form.outlet_ids', [$voucher->outlets->first()->id])
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Edge Cases
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
it('handles voucher with no outlets initially', function () {
|
|
$voucher = Voucher::factory()->create();
|
|
// No outlets attached
|
|
|
|
$outlets = createOutlets(2);
|
|
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.name', 'Test')
|
|
->set('form.code', 'TEST')
|
|
->set('form.tags', 'Test')
|
|
->set('form.type', VoucherType::FIXED->value)
|
|
->set('form.discount_amount', '10000')
|
|
->set('form.summary', 'Test')
|
|
->set('form.start_date', now()->addDay()->format('Y-m-d'))
|
|
->set('form.outlet_ids', $outlets->pluck('id')->toArray())
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$voucher->refresh();
|
|
expect($voucher->outlets)->toHaveCount(2);
|
|
});
|
|
|
|
it('preserves available_count when updating quota', function () {
|
|
$voucher = Voucher::factory()->create([
|
|
'quota' => 100,
|
|
'available_count' => 50, // Some vouchers already used
|
|
]);
|
|
$voucher->outlets()->attach(createOutlets(1)->pluck('id'));
|
|
|
|
// Note: The form doesn't update available_count, only quota
|
|
// This test verifies that available_count is not reset
|
|
mountEditComponent($this->user, $voucher)
|
|
->set('form.quota', '200')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
$voucher->refresh();
|
|
|
|
expect($voucher->quota)->toBe(200);
|
|
// available_count should remain 50 (not reset to 200)
|
|
expect($voucher->available_count)->toBe(50);
|
|
});
|