Waktu
diff --git a/tests/Feature/Studio/Loyalty/RedemptionVerificationTest.php b/tests/Feature/Studio/Loyalty/RedemptionVerificationTest.php
new file mode 100644
index 0000000..ff56ba6
--- /dev/null
+++ b/tests/Feature/Studio/Loyalty/RedemptionVerificationTest.php
@@ -0,0 +1,100 @@
+setupUser();
+
+ $role = Role::firstOrCreate(['name' => 'Owner']);
+ Permission::firstOrCreate(['name' => 'view redemption']);
+ Permission::firstOrCreate(['name' => 'update redemption']);
+ $role->givePermissionTo(['view redemption', 'update redemption']);
+ $this->user->assignRole($role);
+});
+
+it('renders the redemption verification page correctly', function () {
+ $this->actingAs($this->user)
+ ->get(route('studio.loyalty.redemption.index'))
+ ->assertOk()
+ ->assertSeeLivewire(RedemptionVerification::class);
+});
+
+it('can search for a redemption code', function () {
+ $redemption = Redemption::factory()->create(['code' => 'ABC123XYZ']);
+
+ Livewire::actingAs($this->user)
+ ->test(RedemptionVerification::class)
+ ->set('searchCode', 'abc123xyz ') // Testing case insensitivity and trimming
+ ->call('search')
+ ->assertSet('foundRedemption.id', $redemption->id);
+});
+
+it('shows error if redemption code not found', function () {
+ Livewire::actingAs($this->user)
+ ->test(RedemptionVerification::class)
+ ->set('searchCode', 'NONEXISTENT')
+ ->call('search')
+ ->assertSet('foundRedemption', null);
+});
+
+it('can verify a redemption', function () {
+ $redemption = Redemption::factory()->create([
+ 'code' => 'TESTCODE',
+ 'status' => RedemptionStatus::WAITING_PICKUP,
+ 'expires_at' => now()->addDay(),
+ ]);
+
+ Livewire::actingAs($this->user)
+ ->test(RedemptionVerification::class)
+ ->set('searchCode', 'TESTCODE')
+ ->call('search')
+ ->call('verify');
+
+ $redemption->refresh();
+ expect($redemption->status)->toBe(RedemptionStatus::PICKED_UP);
+ expect($redemption->verified_by)->toBe($this->user->id);
+});
+
+it('cannot verify expired redemption', function () {
+ $redemption = Redemption::factory()->create([
+ 'code' => 'EXPIRED',
+ 'status' => RedemptionStatus::WAITING_PICKUP,
+ 'expires_at' => now()->subDay(),
+ ]);
+
+ Livewire::actingAs($this->user)
+ ->test(RedemptionVerification::class)
+ ->set('searchCode', 'EXPIRED')
+ ->call('search')
+ ->call('verify');
+
+ $redemption->refresh();
+ expect($redemption->status)->toBe(RedemptionStatus::EXPIRED);
+});
+
+it('cannot verify already processed redemption', function () {
+ $redemption = Redemption::factory()->verified($this->user)->create(['code' => 'PROCESSED']);
+
+ Livewire::actingAs($this->user)
+ ->test(RedemptionVerification::class)
+ ->set('searchCode', 'PROCESSED')
+ ->call('search')
+ ->call('verify');
+
+ $redemption->refresh();
+ expect($redemption->status)->toBe(RedemptionStatus::PICKED_UP); // Remains picked up
+});
+
+it('cannot access redemption verification without permission', function () {
+ $this->user->roles()->detach();
+ $this->user->permissions()->detach();
+
+ $this->actingAs($this->user)
+ ->get(route('studio.loyalty.redemption.index'))
+ ->assertForbidden();
+});
diff --git a/tests/Feature/Studio/Loyalty/RewardTest.php b/tests/Feature/Studio/Loyalty/RewardTest.php
new file mode 100644
index 0000000..9a42058
--- /dev/null
+++ b/tests/Feature/Studio/Loyalty/RewardTest.php
@@ -0,0 +1,150 @@
+setupUser();
+
+ $role = Role::firstOrCreate(['name' => 'Owner']);
+ Permission::firstOrCreate(['name' => 'view reward']);
+ Permission::firstOrCreate(['name' => 'create reward']);
+ Permission::firstOrCreate(['name' => 'update reward']);
+ Permission::firstOrCreate(['name' => 'delete reward']);
+ $role->givePermissionTo(['view reward', 'create reward', 'update reward', 'delete reward']);
+ $this->user->assignRole($role);
+});
+
+it('renders the reward page correctly', function () {
+ $this->actingAs($this->user)
+ ->get(route('studio.loyalty.reward.index'))
+ ->assertOk()
+ ->assertSeeLivewire(Reward::class);
+});
+
+it('can open create modal', function () {
+ Livewire::actingAs($this->user)
+ ->test(Reward::class)
+ ->call('openModal', 'create', 'Tambah Hadiah')
+ ->assertSet('method', 'create')
+ ->assertSet('modalTitle', 'Tambah Hadiah');
+});
+
+it('shows validation errors on create', function () {
+ Livewire::actingAs($this->user)
+ ->test(Reward::class)
+ ->call('create')
+ ->assertHasErrors([
+ 'form.name' => 'required',
+ 'form.points' => 'required',
+ 'form.start_date' => 'required',
+ 'form.tier_ids' => 'required',
+ ]);
+});
+
+it('can store a new reward', function () {
+ $tier = Tier::factory()->create(['min_spending' => 0]);
+
+ Livewire::actingAs($this->user)
+ ->test(Reward::class)
+ ->set('form.name', 'Hadiah Keren')
+ ->set('form.points', '100')
+ ->set('form.category', RewardCategory::REGULAR->value)
+ ->set('form.is_show', IsShow::SHOW->value)
+ ->set('form.start_date', now()->format('Y-m-d'))
+ ->set('form.tier_ids', [$tier->id])
+ ->call('create')
+ ->assertHasNoErrors()
+ ->assertDispatched('refreshDatatable');
+
+ $this->assertDatabaseHas('rewards', [
+ 'name' => 'Hadiah Keren',
+ 'points' => 100,
+ ]);
+});
+
+it('can open edit modal and load data', function () {
+ $tier = Tier::factory()->create(['min_spending' => 0]);
+ $reward = RewardModel::factory()->create(['name' => 'Old Name', 'points' => 50]);
+ $reward->tiers()->attach($tier);
+
+ Livewire::actingAs($this->user)
+ ->test(Reward::class)
+ ->call('openModal', 'update', 'Ubah Hadiah', $reward->hash_id)
+ ->set('form.reward', $reward)
+ ->set('form.name', 'Old Name')
+ ->set('form.points', '50')
+ ->assertSet('method', 'update')
+ ->assertSet('form.name', 'Old Name')
+ ->assertSet('form.points', '50');
+});
+
+it('can update a reward', function () {
+ $tier = Tier::factory()->create(['min_spending' => 0]);
+ $reward = RewardModel::factory()->create(['name' => 'Old Name', 'points' => 50]);
+ $reward->tiers()->attach($tier);
+
+ Livewire::actingAs($this->user)
+ ->test(Reward::class)
+ ->call('openModal', 'update', 'Ubah Hadiah', $reward->hash_id)
+ ->set('form.reward', $reward)
+ ->set('form.name', 'Updated Name')
+ ->set('form.points', '75')
+ ->set('form.category', $reward->category->value)
+ ->set('form.is_show', $reward->is_show->value)
+ ->set('form.start_date', $reward->start_date)
+ ->set('form.tier_ids', [$tier->id])
+ ->call('update')
+ ->assertHasNoErrors()
+ ->assertDispatched('refreshDatatable');
+
+ $this->assertDatabaseHas('rewards', [
+ 'id' => $reward->id,
+ 'name' => 'Updated Name',
+ 'points' => 75,
+ ]);
+});
+
+it('cannot update a reward if redemptions exist', function () {
+ $reward = RewardModel::factory()->create();
+ Redemption::factory()->create(['reward_id' => $reward->id]);
+
+ Livewire::actingAs($this->user)
+ ->test(Reward::class)
+ ->call('openModal', 'update', 'Ubah Hadiah', $reward->hash_id)
+ ->set('form.reward', $reward)
+ ->set('form.name', 'Attempt Update')
+ ->call('update')
+ ->assertHasNoErrors();
+
+ // Verify it didn't change in DB
+ $this->assertDatabaseMissing('rewards', ['id' => $reward->id, 'name' => 'Attempt Update']);
+});
+
+it('can delete a reward', function () {
+ $reward = RewardModel::factory()->create();
+
+ Livewire::actingAs($this->user)
+ ->test(Reward::class)
+ ->call('delete', $reward->id)
+ ->assertHasNoErrors()
+ ->assertDispatched('refreshDatatable');
+
+ $this->assertSoftDeleted('rewards', ['id' => $reward->id]);
+});
+
+it('cannot access reward page without permission', function () {
+ $this->user->roles()->detach();
+ $this->user->permissions()->detach();
+
+ $this->actingAs($this->user)
+ ->get(route('studio.loyalty.reward.index'))
+ ->assertForbidden();
+});