each(fn ($role) => Role::create(['name' => $role])); $this->seed(TierSeeder::class); Notification::fake(); }); /* |-------------------------------------------------------------------------- | Helpers |-------------------------------------------------------------------------- */ function fillStep1(?string $email = null, ?string $username = null, ?string $password = null) { return Livewire::test(Register::class) ->set('form.email', $email ?? 'info.pangestuyoga@gmail.com') ->set('form.username', $username ?? 'pangestu') ->set('form.password', $password ?? 'Aggygga13@'); } function fillStep2(?string $name = null, ?string $phoneNumber = null, $gender = null, ?string $referralCode = null) { $component = fillStep1() ->set('form.name', $name ?? 'Test User') ->set('form.phone_number', $phoneNumber ?? '0812 3456 789') ->set('form.gender', $gender ?? Gender::MALE->value); if ($referralCode !== null) { $component->set('form.referral_code', $referralCode); } return $component; } function attemptRegistration(?string $referralCode = null) { fillStep1()->call('nextStep'); return fillStep2(referralCode: $referralCode) ->call('register'); } function makeReferralCode(): ReferralCode { $user = User::factory() ->active() ->has(Employee::factory()->male()->permanent()) ->has(ReferralCode::factory()) ->create(); $user->assignRole('Customer'); return $user->referralCode; } /* |-------------------------------------------------------------------------- | Component Rendering & Mount |-------------------------------------------------------------------------- */ it('renders register component successfully', function () { Livewire::test(Register::class) ->assertViewIs('livewire.auth.register') ->assertSet('currentStep', 1) ->assertSet('totalSteps', 2) ->assertSet('form.email', '') ->assertSet('form.username', '') ->assertSet('form.password', ''); }); it('mounts component with correct initial state', function () { Livewire::test(Register::class) ->assertSet('currentStep', 1) ->assertSet('totalSteps', 2) ->assertCount('steps', 2) ->assertSet('steps.0.name', 'Kredensial') ->assertSet('steps.1.name', 'Informasi'); }); /* |-------------------------------------------------------------------------- | Step 1 Validation - Required Fields |-------------------------------------------------------------------------- */ it('validates step 1 required fields', function () { Livewire::test(Register::class) ->set('form.email', '') ->set('form.username', '') ->set('form.password', '') ->call('nextStep') ->assertHasErrors(['form.email', 'form.username', 'form.password']); }); it('validates email field is required in step 1', function () { fillStep1('') ->call('nextStep') ->assertHasErrors(['form.email']); }); it('validates username field is required in step 1', function () { fillStep1(username: '') ->call('nextStep') ->assertHasErrors(['form.username']); }); it('validates password field is required in step 1', function () { fillStep1(password: '') ->call('nextStep') ->assertHasErrors(['form.password']); }); /* |-------------------------------------------------------------------------- | Step 1 Validation - Email Rules |-------------------------------------------------------------------------- */ it('validates email format in step 1', function () { fillStep1('invalid-email') ->call('nextStep') ->assertHasErrors(['form.email']); }); it('validates email is unique in step 1', function () { $user = User::factory()->create(); fillStep1($user->email) ->call('nextStep') ->assertHasErrors(['form.email']); }); /* |-------------------------------------------------------------------------- | Step 1 Validation - Username Rules |-------------------------------------------------------------------------- */ it('validates username is unique in step 1', function () { $user = User::factory()->create(); fillStep1(username: $user->username) ->call('nextStep') ->assertHasErrors(['form.username']); }); it('validates username is alpha numeric in step 1', function () { fillStep1(username: 'test user!') ->call('nextStep') ->assertHasErrors(['form.username']); }); it('validates username minimum length in step 1', function () { fillStep1(username: 'test') ->call('nextStep') ->assertHasErrors(['form.username']); }); it('validates username maximum length in step 1', function () { fillStep1(username: str_repeat('a', 21)) ->call('nextStep') ->assertHasErrors(['form.username']); }); /* |-------------------------------------------------------------------------- | Step 1 Validation - Password Rules |-------------------------------------------------------------------------- */ it('validates password minimum length in step 1', function () { fillStep1(password: 'Pass1!') ->call('nextStep') ->assertHasErrors(['form.password']); }); it('validates password contains uppercase and lowercase letters in step 1', function () { fillStep1(password: 'password123!') ->call('nextStep') ->assertHasErrors(['form.password']); }); it('validates password contains numbers in step 1', function () { fillStep1(password: 'Password!') ->call('nextStep') ->assertHasErrors(['form.password']); }); it('validates password contains symbols in step 1', function () { fillStep1(password: 'Password123') ->call('nextStep') ->assertHasErrors(['form.password']); }); /* |-------------------------------------------------------------------------- | Step 1 Flow Control |-------------------------------------------------------------------------- */ it('moves to step 2 when step 1 validation passes', function () { fillStep1() ->call('nextStep') ->assertHasNoErrors() ->assertSet('currentStep', 2); }); /* |-------------------------------------------------------------------------- | Step 2 Validation - Required Fields |-------------------------------------------------------------------------- */ it('validates step 2 required fields', function () { fillStep1() ->set('form.name', '') ->set('form.phone_number', '') ->set('form.gender', '') ->call('register') ->assertHasErrors(['form.name', 'form.phone_number', 'form.gender']); }); it('validates name field is required in step 2', function () { fillStep2('') ->call('register') ->assertHasErrors(['form.name']); }); it('validates phone number field is required in step 2', function () { fillStep2(phoneNumber: '') ->call('register') ->assertHasErrors(['form.phone_number']); }); it('validates gender field is required in step 2', function () { fillStep2(gender: '') ->call('register') ->assertHasErrors(['form.gender']); }); /* |-------------------------------------------------------------------------- | Step 2 Validation - Field Format |-------------------------------------------------------------------------- */ it('validates phone number format in step 2', function () { fillStep2(phoneNumber: '08123456789') ->call('register') ->assertHasErrors(['form.phone_number']); }); it('validates gender is valid enum value in step 2', function () { fillStep2() ->set('form.gender', 3) ->call('register') ->assertHasErrors(['form.gender']); }); it('validates referral code exists when provided in step 2', function () { fillStep2(referralCode: 'INVALIDCODE') ->call('register') ->assertHasErrors(['form.referral_code']); }); /* |-------------------------------------------------------------------------- | Step 2 Flow Control |-------------------------------------------------------------------------- */ it('can navigate back to step 1 from step 2', function () { fillStep1() ->call('nextStep') ->assertSet('currentStep', 2) ->call('prevStep') ->assertSet('currentStep', 1); }); it('cannot navigate before step 1', function () { Livewire::test(Register::class) ->assertSet('currentStep', 1) ->call('prevStep') ->assertSet('currentStep', 1); }); it('can update step directly', function () { Livewire::test(Register::class) ->call('updateStep', 2) ->assertSet('currentStep', 1); }); it('validates step 1 when updating step directly to step 2', function () { Livewire::test(Register::class) ->set('form.email', '') ->set('form.username', '') ->set('form.password', '') ->call('updateStep', 2) ->assertHasErrors(['form.email', 'form.username', 'form.password']) ->assertSet('currentStep', 1); }); /* |-------------------------------------------------------------------------- | Registration Success Cases |-------------------------------------------------------------------------- */ it('successfully registers a new user without referral code', function () { attemptRegistration() ->assertHasNoErrors() ->assertRedirect('/member/overview'); expect(User::where('email', 'info.pangestuyoga@gmail.com')->exists())->toBeTrue(); expect(Customer::whereHas('user', fn ($q) => $q->where('email', 'info.pangestuyoga@gmail.com'))->exists())->toBeTrue(); $user = User::where('email', 'info.pangestuyoga@gmail.com')->first(); expect($user->status)->toBe(UserStatus::ACTIVE); expect($user->hasRole('Customer'))->toBeTrue(); expect(Membership::where('user_id', $user->id)->exists())->toBeTrue(); expect(PointRecord::where('user_id', $user->id)->exists())->toBeTrue(); expect(ReferralCode::where('user_id', $user->id)->exists())->toBeTrue(); expect(auth()->check())->toBeTrue(); }); it('successfully registers a new user with referral code', function () { $referralCode = makeReferralCode(); attemptRegistration($referralCode->code) ->assertHasNoErrors() ->assertRedirect('/member/overview'); $user = User::where('email', 'info.pangestuyoga@gmail.com')->first(); expect(ReferralUsage::where('user_id', $user->id)->exists())->toBeTrue(); $referralCode->refresh(); expect($referralCode->uses_count)->toBe(1); }); /* |-------------------------------------------------------------------------- | Registration Data Creation |-------------------------------------------------------------------------- */ it('creates membership with correct tier and points', function () { attemptRegistration() ->assertHasNoErrors(); $user = User::where('email', 'info.pangestuyoga@gmail.com')->first(); $membership = Membership::where('user_id', $user->id)->first(); $tier = Tier::orderBy('min_points')->first(); expect($membership->tier_id)->toBe($tier->id); expect($membership->tier_points)->toBe(10); }); it('creates point record for registration', function () { attemptRegistration() ->assertHasNoErrors(); $user = User::where('email', 'info.pangestuyoga@gmail.com')->first(); $pointRecord = PointRecord::where('user_id', $user->id)->first(); expect($pointRecord->description)->toBe('Pendaftaran'); expect($pointRecord->change)->toBe(10); expect($pointRecord->is_addition)->toBeTrue(); }); /* |-------------------------------------------------------------------------- | Real-time Validation |-------------------------------------------------------------------------- */ it('validates individual field on update for step 1', function () { Livewire::test(Register::class) ->set('form.email', 'invalid-email') ->assertHasErrors(['form.email']) ->set('form.email', 'valid@example.com') ->assertHasNoErrors(['form.email']); }); it('validates individual field on update for step 2', function () { fillStep1()->call('nextStep') ->set('form.phone_number', 'invalid') ->assertHasErrors(['form.phone_number']) ->set('form.phone_number', '0812 3456 789') ->assertHasNoErrors(['form.phone_number']); });