From eca3a7cd7b3df1f0359690342d315bded582c729 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Wed, 3 Dec 2025 20:39:20 +0700 Subject: [PATCH] feat(test): membuat test untuk login dan register --- tests/Feature/Livewire/Auth/LoginTest.php | 250 +++++++++++ tests/Feature/Livewire/Auth/RegisterTest.php | 417 +++++++++++++++++++ 2 files changed, 667 insertions(+) create mode 100644 tests/Feature/Livewire/Auth/LoginTest.php create mode 100644 tests/Feature/Livewire/Auth/RegisterTest.php diff --git a/tests/Feature/Livewire/Auth/LoginTest.php b/tests/Feature/Livewire/Auth/LoginTest.php new file mode 100644 index 0000000..43d4dd8 --- /dev/null +++ b/tests/Feature/Livewire/Auth/LoginTest.php @@ -0,0 +1,250 @@ +each(fn ($role) => Role::create(['name' => $role])); + + $this->correctPassword = config('myconfig.password_default'); + + $this->wrongPassword = 'wrongpassword'; +}); + +/* +|-------------------------------------------------------------------------- +| Helpers +|-------------------------------------------------------------------------- +*/ + +function makeUser(?string $role = null, bool $isActive = true): User +{ + $factory = User::factory(); + + $factory = $isActive + ? $factory->active() + : $factory->inactive(); + + $user = $factory + ->has(Employee::factory()->male()->permanent()) + ->create(); + + if ($role) { + $user->assignRole($role); + } + + return $user; +} + +function attemptLogin(string $login, string $password) +{ + return Livewire::test(Login::class) + ->set('login', $login) + ->set('password', $password) + ->call('auth'); +} + +/* +|-------------------------------------------------------------------------- +| Component Rendering & Mount +|-------------------------------------------------------------------------- +*/ + +it('renders login component successfully', function () { + Livewire::test(Login::class) + ->assertViewIs('livewire.auth.login') + ->assertSet('login', '') + ->assertSet('password', ''); +}); + +it('mounts component without query parameters', function () { + Livewire::test(Login::class) + ->assertSet('login', '') + ->assertSet('password', ''); +}); + +it('mounts component with message query parameter', function () { + $message = 'Test message'; + + Livewire::withQueryParams(['message' => $message]) + ->test(Login::class) + ->assertSet('login', '') + ->assertSet('password', ''); +}); + +/* +|-------------------------------------------------------------------------- +| Validation Rules +|-------------------------------------------------------------------------- +*/ + +it('validates required fields', function () { + Livewire::test(Login::class) + ->set('login', '') + ->set('password', '') + ->call('auth') + ->assertHasErrors(['login', 'password']); +}); + +it('validates login field is required', function () { + Livewire::test(Login::class) + ->set('login', '') + ->set('password', 'password123') + ->call('auth') + ->assertHasErrors(['login']); +}); + +it('validates password field is required', function () { + Livewire::test(Login::class) + ->set('login', 'testuser') + ->set('password', '') + ->call('auth') + ->assertHasErrors(['password']); +}); + +/* +|-------------------------------------------------------------------------- +| Authentication Failures +|-------------------------------------------------------------------------- +*/ + +it('fails authentication with wrong credentials', function () { + attemptLogin('nonexistent@example.com', $this->correctPassword); + + expect(auth()->check())->toBeFalse(); +}); + +it('fails authentication with non-existent user', function () { + attemptLogin('nonexistent@example.com', $this->wrongPassword); + + expect(auth()->check())->toBeFalse(); +}); + +/* +|-------------------------------------------------------------------------- +| Authentication Works (email & username) +|-------------------------------------------------------------------------- +*/ + +it('successfully authenticates with email', function () { + $user = makeUser('Developer'); + + attemptLogin($user->email, $this->correctPassword)->assertRedirect('/studio/dashboard/overview'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +it('successfully authenticates with username', function () { + $user = makeUser('Developer'); + + attemptLogin($user->username, $this->correctPassword)->assertRedirect('/studio/dashboard/overview'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +/* +|-------------------------------------------------------------------------- +| Prevent Inactive User Login +|-------------------------------------------------------------------------- +*/ + +it('prevents login for inactive user', function () { + $user = makeUser('Developer', false); + + attemptLogin($user->email, $this->correctPassword); + + expect(auth()->check())->toBeFalse(); +}); + +/* +|-------------------------------------------------------------------------- +| Role Redirect Tests +|-------------------------------------------------------------------------- +*/ + +it('redirects developer to studio dashboard', function () { + $user = makeUser('Developer'); + + attemptLogin($user->email, $this->correctPassword)->assertRedirect('/studio/dashboard/overview'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +it('redirects owner to studio dashboard', function () { + $user = makeUser('Owner'); + + attemptLogin($user->email, $this->correctPassword)->assertRedirect('/studio/dashboard/overview'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +it('redirects leader to studio dashboard', function () { + $user = makeUser('Leader'); + + attemptLogin($user->email, $this->correctPassword)->assertRedirect('/studio/dashboard/overview'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +it('redirects admin to studio dashboard', function () { + $user = makeUser('Admin'); + + attemptLogin($user->email, $this->correctPassword)->assertRedirect('/studio/dashboard/overview'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +it('redirects partner to partner overview', function () { + $user = makeUser('Partner'); + + attemptLogin($user->email, $this->correctPassword)->assertRedirect('/partner/overview'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +it('redirects customer to member overview', function () { + $user = makeUser('Customer'); + + attemptLogin($user->email, $this->correctPassword)->assertRedirect('/member/overview'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +/* +|-------------------------------------------------------------------------- +| Edge Case +|-------------------------------------------------------------------------- +*/ + +it('redirects user without role to login', function () { + $user = makeUser(); + + attemptLogin($user->email, $this->correctPassword)->assertRedirect('/login'); + + expect(auth()->check())->toBeTrue(); + expect(auth()->id())->toBe($user->id); +}); + +it('regenerates session on successful login', function () { + $user = makeUser('Developer'); + + attemptLogin($user->email, $this->correctPassword); + + expect(auth()->check())->toBeTrue(); + expect(auth()->user()->id)->toBe($user->id); +}); diff --git a/tests/Feature/Livewire/Auth/RegisterTest.php b/tests/Feature/Livewire/Auth/RegisterTest.php new file mode 100644 index 0000000..e9e969e --- /dev/null +++ b/tests/Feature/Livewire/Auth/RegisterTest.php @@ -0,0 +1,417 @@ +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']); +});