name = 'Test User'; $this->email = 'test@example.com'; } public function updateProfile(): void { $this->validate([ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email', 'max:255'], ]); $this->dispatched[] = 'profile-updated'; $this->toasts[] = ['text' => 'Profile updated successfully.', 'variant' => 'success']; } public function updatePreferences(): void { $this->validate([ 'locale' => ['required', 'string'], 'timezone' => ['required', 'timezone'], 'time_format' => ['required', 'in:12,24'], 'week_starts_on' => ['required', 'in:0,1'], ]); $this->dispatched[] = 'preferences-updated'; $this->toasts[] = ['text' => 'Preferences updated.', 'variant' => 'success']; } public function updatePassword(): void { $this->validate([ 'current_password' => ['required'], 'new_password' => ['required', 'confirmed', 'min:8'], ]); $this->current_password = ''; $this->new_password = ''; $this->new_password_confirmation = ''; $this->dispatched[] = 'password-updated'; $this->toasts[] = ['text' => 'Password updated.', 'variant' => 'success']; } public function enableTwoFactor(): void { $this->toasts[] = ['text' => 'Two-factor upgrading.', 'variant' => 'warning']; } public function requestAccountDeletion(): void { $this->showDeleteConfirmation = false; $this->deleteReason = ''; $this->toasts[] = ['text' => 'Deletion scheduled.', 'variant' => 'warning']; } public function cancelAccountDeletion(): void { $this->toasts[] = ['text' => 'Deletion cancelled.', 'variant' => 'success']; } public function render(): string { return <<<'HTML'
Section: {{ $activeSection }} @if($showDeleteConfirmation)
Delete Confirmation
@endif
HTML; } } // ============================================================================= // Tab Navigation Tests // ============================================================================= describe('Settings tab navigation', function () { it('defaults to profile section', function () { Livewire::test(SettingsModalDouble::class) ->assertSet('activeSection', 'profile'); }); it('switches to preferences section', function () { Livewire::test(SettingsModalDouble::class) ->set('activeSection', 'preferences') ->assertSet('activeSection', 'preferences'); }); it('switches to security section', function () { Livewire::test(SettingsModalDouble::class) ->set('activeSection', 'security') ->assertSet('activeSection', 'security'); }); it('switches to danger zone section', function () { Livewire::test(SettingsModalDouble::class) ->set('activeSection', 'danger') ->assertSet('activeSection', 'danger'); }); it('renders active section label', function () { Livewire::test(SettingsModalDouble::class) ->set('activeSection', 'preferences') ->assertSee('Section: preferences'); }); it('cycles through all sections', function () { $component = Livewire::test(SettingsModalDouble::class); foreach (['profile', 'preferences', 'security', 'danger'] as $section) { $component->set('activeSection', $section) ->assertSet('activeSection', $section); } }); }); // ============================================================================= // Profile Update Tests // ============================================================================= describe('Settings profile update', function () { it('loads initial profile data on mount', function () { Livewire::test(SettingsModalDouble::class) ->assertSet('name', 'Test User') ->assertSet('email', 'test@example.com'); }); it('validates required name', function () { Livewire::test(SettingsModalDouble::class) ->set('name', '') ->call('updateProfile') ->assertHasErrors(['name']); }); it('validates required email', function () { Livewire::test(SettingsModalDouble::class) ->set('email', '') ->call('updateProfile') ->assertHasErrors(['email']); }); it('validates email format', function () { Livewire::test(SettingsModalDouble::class) ->set('email', 'not-an-email') ->call('updateProfile') ->assertHasErrors(['email']); }); it('validates name max length', function () { Livewire::test(SettingsModalDouble::class) ->set('name', str_repeat('A', 256)) ->call('updateProfile') ->assertHasErrors(['name']); }); it('succeeds with valid profile data', function () { Livewire::test(SettingsModalDouble::class) ->set('name', 'Jane Doe') ->set('email', 'jane@example.com') ->call('updateProfile') ->assertHasNoErrors() ->assertSet('dispatched', ['profile-updated']); }); it('records success toast on profile update', function () { Livewire::test(SettingsModalDouble::class) ->set('name', 'Jane Doe') ->set('email', 'jane@example.com') ->call('updateProfile') ->assertSet('toasts', fn ($toasts) => count($toasts) === 1 && $toasts[0]['variant'] === 'success'); }); }); // ============================================================================= // Preferences Update Tests // ============================================================================= describe('Settings preferences update', function () { it('has correct default preferences', function () { Livewire::test(SettingsModalDouble::class) ->assertSet('locale', 'en_GB') ->assertSet('timezone', 'Europe/London') ->assertSet('time_format', 12) ->assertSet('week_starts_on', 1); }); it('validates timezone', function () { Livewire::test(SettingsModalDouble::class) ->set('timezone', 'Invalid/Zone') ->call('updatePreferences') ->assertHasErrors(['timezone']); }); it('validates time_format values', function () { Livewire::test(SettingsModalDouble::class) ->set('time_format', 99) ->call('updatePreferences') ->assertHasErrors(['time_format']); }); it('validates week_starts_on values', function () { Livewire::test(SettingsModalDouble::class) ->set('week_starts_on', 5) ->call('updatePreferences') ->assertHasErrors(['week_starts_on']); }); it('accepts valid 24-hour format', function () { Livewire::test(SettingsModalDouble::class) ->set('time_format', 24) ->call('updatePreferences') ->assertHasNoErrors(['time_format']); }); it('accepts Monday start (1) and Sunday start (0)', function () { Livewire::test(SettingsModalDouble::class) ->set('week_starts_on', 0) ->call('updatePreferences') ->assertHasNoErrors(['week_starts_on']); Livewire::test(SettingsModalDouble::class) ->set('week_starts_on', 1) ->call('updatePreferences') ->assertHasNoErrors(['week_starts_on']); }); it('dispatches preferences-updated on success', function () { Livewire::test(SettingsModalDouble::class) ->set('locale', 'en_US') ->set('timezone', 'America/New_York') ->set('time_format', 24) ->set('week_starts_on', 0) ->call('updatePreferences') ->assertHasNoErrors() ->assertSet('dispatched', ['preferences-updated']); }); }); // ============================================================================= // Password Change Tests // ============================================================================= describe('Settings password change', function () { it('validates current password required', function () { Livewire::test(SettingsModalDouble::class) ->set('current_password', '') ->set('new_password', 'newpass123') ->set('new_password_confirmation', 'newpass123') ->call('updatePassword') ->assertHasErrors(['current_password']); }); it('validates new password required', function () { Livewire::test(SettingsModalDouble::class) ->set('current_password', 'oldpass') ->set('new_password', '') ->call('updatePassword') ->assertHasErrors(['new_password']); }); it('validates password confirmation matches', function () { Livewire::test(SettingsModalDouble::class) ->set('current_password', 'oldpass') ->set('new_password', 'newpass123') ->set('new_password_confirmation', 'different') ->call('updatePassword') ->assertHasErrors(['new_password']); }); it('validates minimum password length', function () { Livewire::test(SettingsModalDouble::class) ->set('current_password', 'oldpass') ->set('new_password', 'short') ->set('new_password_confirmation', 'short') ->call('updatePassword') ->assertHasErrors(['new_password']); }); it('clears password fields on success', function () { Livewire::test(SettingsModalDouble::class) ->set('current_password', 'oldpassword') ->set('new_password', 'newpassword123') ->set('new_password_confirmation', 'newpassword123') ->call('updatePassword') ->assertHasNoErrors() ->assertSet('current_password', '') ->assertSet('new_password', '') ->assertSet('new_password_confirmation', ''); }); it('dispatches password-updated event on success', function () { Livewire::test(SettingsModalDouble::class) ->set('current_password', 'oldpassword') ->set('new_password', 'newpassword123') ->set('new_password_confirmation', 'newpassword123') ->call('updatePassword') ->assertSet('dispatched', ['password-updated']); }); }); // ============================================================================= // Account Deletion Tests // ============================================================================= describe('Settings account deletion', function () { it('shows delete confirmation modal', function () { Livewire::test(SettingsModalDouble::class) ->set('showDeleteConfirmation', true) ->assertSee('Delete Confirmation'); }); it('hides delete confirmation by default', function () { Livewire::test(SettingsModalDouble::class) ->assertSet('showDeleteConfirmation', false) ->assertDontSee('Delete Confirmation'); }); it('resets state when deletion requested', function () { Livewire::test(SettingsModalDouble::class) ->set('showDeleteConfirmation', true) ->set('deleteReason', 'No longer needed') ->call('requestAccountDeletion') ->assertSet('showDeleteConfirmation', false) ->assertSet('deleteReason', ''); }); it('records warning toast for deletion', function () { Livewire::test(SettingsModalDouble::class) ->call('requestAccountDeletion') ->assertSet('toasts', fn ($toasts) => $toasts[0]['variant'] === 'warning'); }); it('records success toast for deletion cancellation', function () { Livewire::test(SettingsModalDouble::class) ->call('cancelAccountDeletion') ->assertSet('toasts', fn ($toasts) => $toasts[0]['variant'] === 'success'); }); it('has delete account feature enabled by default', function () { Livewire::test(SettingsModalDouble::class) ->assertSet('isDeleteAccountEnabled', true); }); }); // ============================================================================= // Two-Factor Authentication Tests // ============================================================================= describe('Settings two-factor authentication', function () { it('has two-factor disabled by default', function () { Livewire::test(SettingsModalDouble::class) ->assertSet('isTwoFactorEnabled', false) ->assertSet('userHasTwoFactorEnabled', false) ->assertSet('showTwoFactorSetup', false); }); it('records warning toast when enabling two-factor (not yet implemented)', function () { Livewire::test(SettingsModalDouble::class) ->call('enableTwoFactor') ->assertSet('toasts', fn ($toasts) => $toasts[0]['variant'] === 'warning'); }); });