From 0795f8b0d76c98beb2566145a7aef181acb86141 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sun, 4 Jan 2026 22:00:17 +0700 Subject: [PATCH] feat: Add feature tests for member membership, overview, redeem points, and vouchers, and enable factory for the Membership model. --- app/Models/Membership.php | 3 +- tests/Feature/Member/MembershipTest.php | 57 +++++++++++++ tests/Feature/Member/OverviewTest.php | 64 ++++++++++++++ tests/Feature/Member/RedeemPointTest.php | 104 +++++++++++++++++++++++ tests/Feature/Member/VoucherTest.php | 36 ++++++++ 5 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 tests/Feature/Member/MembershipTest.php create mode 100644 tests/Feature/Member/OverviewTest.php create mode 100644 tests/Feature/Member/RedeemPointTest.php create mode 100644 tests/Feature/Member/VoucherTest.php diff --git a/app/Models/Membership.php b/app/Models/Membership.php index ee7283a..1ea89a5 100644 --- a/app/Models/Membership.php +++ b/app/Models/Membership.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; @@ -9,7 +10,7 @@ class Membership extends Model { - use HashableId, SoftDeletes; + use HasFactory, HashableId, SoftDeletes; protected $guarded = ['id']; diff --git a/tests/Feature/Member/MembershipTest.php b/tests/Feature/Member/MembershipTest.php new file mode 100644 index 0000000..8fbd814 --- /dev/null +++ b/tests/Feature/Member/MembershipTest.php @@ -0,0 +1,57 @@ +setupUser(); +}); + +it('renders the membership page', function () { + $this->actingAs($this->user); + + $tier = Tier::factory()->create(['min_spending' => 0, 'name' => 'Bronze']); + MembershipModel::factory()->for($this->user)->create([ + 'tier_id' => $tier->id, + 'reward_points' => 100, + 'total_spending' => 50000, + ]); + + Livewire::test(Membership::class) + ->assertStatus(200) + ->assertSee('Membership') + ->assertSee('Bronze') + ->assertSee('100'); +}); + +it('shows max tier if already at highest tier', function () { + $this->actingAs($this->user); + + $tier = Tier::factory()->create(['min_spending' => 0, 'name' => 'Bronze']); + MembershipModel::factory()->for($this->user)->create([ + 'tier_id' => $tier->id, + 'total_spending' => 1000000, + ]); + + Livewire::test(Membership::class) + ->assertSet('nextTierName', 'Maksimal') + ->assertSet('progress', '100'); +}); + +it('calculates progress to next tier correctly', function () { + $this->actingAs($this->user); + + $bronze = Tier::factory()->create(['min_spending' => 0, 'name' => 'Bronze']); + $silver = Tier::factory()->create(['min_spending' => 100000, 'name' => 'Silver']); + + MembershipModel::factory()->for($this->user)->create([ + 'tier_id' => $bronze->id, + 'total_spending' => 50000, + ]); + + Livewire::test(Membership::class) + ->assertSet('nextTierName', 'Silver') + ->assertSet('progress', 50); +}); diff --git a/tests/Feature/Member/OverviewTest.php b/tests/Feature/Member/OverviewTest.php new file mode 100644 index 0000000..af5a8fb --- /dev/null +++ b/tests/Feature/Member/OverviewTest.php @@ -0,0 +1,64 @@ +setupUser(); +}); + +it('renders the member overview page', function () { + $this->actingAs($this->user); + + Livewire::test(Overview::class) + ->assertStatus(200) + ->assertSee('Selamat datang'); +}); + +it('shows registration form if user is not a member', function () { + $this->actingAs($this->user); + + Livewire::test(Overview::class) + ->assertSet('hasMemberData', false) + ->assertSee('Anda belum terdaftar menjadi member'); +}); + +it('can register as a member', function () { + $this->actingAs($this->user); + + // We need at least one tier for membership creation + Tier::factory()->create(['min_spending' => 0]); + + Livewire::test(Overview::class) + ->set('form.user_id', $this->user->id) + ->set('form.name', 'Test Member') + ->set('form.phone_number', '0812 3456 7890') + ->set('form.gender', Gender::MALE->value) + ->call('save') + ->assertHasNoErrors() + ->assertSet('hasMemberData', true); + + $this->assertDatabaseHas('customers', [ + 'user_id' => $this->user->id, + 'name' => 'Test Member', + 'phone_number' => '0812 3456 7890', + 'gender' => Gender::MALE->value, + ]); + + $this->assertDatabaseHas('memberships', [ + 'user_id' => $this->user->id, + ]); +}); + +it('shows member data if user is already a member', function () { + $this->actingAs($this->user); + + Customer::factory()->for($this->user)->create(); + + Livewire::test(Overview::class) + ->assertSet('hasMemberData', true) + ->assertDontSee('Daftar Member'); +}); diff --git a/tests/Feature/Member/RedeemPointTest.php b/tests/Feature/Member/RedeemPointTest.php new file mode 100644 index 0000000..f09f47c --- /dev/null +++ b/tests/Feature/Member/RedeemPointTest.php @@ -0,0 +1,104 @@ +setupUser(); + $this->tier = Tier::factory()->create(['min_spending' => 0]); + $this->membership = Membership::factory()->for($this->user)->create([ + 'tier_id' => $this->tier->id, + 'reward_points' => 1000, + ]); +}); + +it('renders the redeem point page', function () { + $this->actingAs($this->user); + + Livewire::test(RedeemPoint::class) + ->assertStatus(200) + ->assertSee('Hadiah'); +}); + +it('shows rewards available for user tier', function () { + $this->actingAs($this->user); + + $reward = Reward::factory()->create([ + 'name' => 'Free Perfume', + 'points' => 100, + 'is_show' => IsShow::SHOW, + 'start_date' => now()->subDay(), + 'end_date' => now()->addDay(), + 'category' => RewardCategory::REGULAR, + ]); + $reward->tiers()->attach($this->tier->id); + + Livewire::test(RedeemPoint::class) + ->assertSee('Free Perfume'); +}); + +it('can redeem a reward', function () { + $this->actingAs($this->user); + + $reward = Reward::factory()->create([ + 'name' => 'Free Perfume', + 'points' => 100, + 'stock' => 10, + 'is_show' => IsShow::SHOW, + 'start_date' => now()->subDay(), + 'category' => RewardCategory::REGULAR, + ]); + $reward->tiers()->attach($this->tier->id); + + Livewire::test(RedeemPoint::class) + ->call('redeem', $reward) + ->assertHasNoErrors(); + + $this->assertDatabaseHas('redemptions', [ + 'user_id' => $this->user->id, + 'reward_id' => $reward->id, + 'points_spent' => 100, + ]); + + $this->assertEquals(900, $this->user->refresh()->membership->reward_points); + $this->assertEquals(9, $reward->refresh()->stock); +}); + +it('cannot redeem reward if points are insufficient', function () { + $this->actingAs($this->user); + $this->membership->update(['reward_points' => 50]); + + $reward = Reward::factory()->create([ + 'points' => 100, + 'is_show' => IsShow::SHOW, + 'start_date' => now()->subDay(), + ]); + $reward->tiers()->attach($this->tier->id); + + Livewire::test(RedeemPoint::class) + ->call('redeem', $reward); + + $this->assertEquals(50, $this->user->refresh()->membership->reward_points); +}); + +it('cannot redeem reward if stock is empty', function () { + $this->actingAs($this->user); + + $reward = Reward::factory()->create([ + 'points' => 100, + 'stock' => 0, + 'is_show' => IsShow::SHOW, + 'start_date' => now()->subDay(), + ]); + $reward->tiers()->attach($this->tier->id); + + Livewire::test(RedeemPoint::class) + ->call('redeem', $reward); + + $this->assertEquals(1000, $this->user->refresh()->membership->reward_points); +}); diff --git a/tests/Feature/Member/VoucherTest.php b/tests/Feature/Member/VoucherTest.php new file mode 100644 index 0000000..8fd4a28 --- /dev/null +++ b/tests/Feature/Member/VoucherTest.php @@ -0,0 +1,36 @@ +setupUser(); +}); + +it('renders the voucher page', function () { + $this->actingAs($this->user); + + Livewire::test(Voucher::class) + ->assertStatus(200) + ->assertSee('Voucher'); +}); + +it('shows vouchers owned by the user', function () { + $this->actingAs($this->user); + + $voucher = VoucherModel::factory()->create([ + 'name' => 'Diskon Awal Tahun', + 'code' => 'NEWYEAR2024', + 'type' => VoucherType::FIXED, + 'discount_amount' => 10000, + ]); + + $this->user->vouchers()->attach($voucher->id); + + Livewire::test(Voucher::class) + ->assertSee('Diskon Awal Tahun') + ->assertSee('NEWYEAR2024') + ->assertSee('10.000'); +});