319 lines
10 KiB
PHP
319 lines
10 KiB
PHP
<?php
|
|
|
|
use App\Enums\ConsultationStatus;
|
|
use App\Models\Consultation;
|
|
use App\Models\User;
|
|
use App\Models\WorkingHour;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
use Livewire\Volt\Volt;
|
|
|
|
beforeEach(function () {
|
|
Mail::fake();
|
|
RateLimiter::clear('guest-booking:127.0.0.1');
|
|
});
|
|
|
|
test('guest can view booking page', function () {
|
|
$this->get(route('booking'))
|
|
->assertOk()
|
|
->assertSee(__('booking.request_consultation'));
|
|
});
|
|
|
|
test('logged in user sees message to book from dashboard', function () {
|
|
$user = User::factory()->client()->create();
|
|
|
|
$this->actingAs($user)
|
|
->get(route('booking'))
|
|
->assertOk()
|
|
->assertSee(__('booking.logged_in_title'))
|
|
->assertSee(__('booking.logged_in_message'))
|
|
->assertSee(__('booking.go_to_dashboard'));
|
|
});
|
|
|
|
test('guest booking page shows calendar', function () {
|
|
$this->get(route('booking'))
|
|
->assertOk()
|
|
->assertSee(__('booking.guest_intro'));
|
|
});
|
|
|
|
test('guest can select a slot and see contact form', function () {
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
$date = now()->addDay()->format('Y-m-d');
|
|
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', $date, '09:00')
|
|
->assertSet('selectedDate', $date)
|
|
->assertSet('selectedTime', '09:00')
|
|
->assertSee(__('booking.guest_name'));
|
|
});
|
|
|
|
test('guest can submit booking request', function () {
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
$date = now()->addDay()->format('Y-m-d');
|
|
|
|
$component = Volt::test('pages.booking')
|
|
->call('selectSlot', $date, '09:00')
|
|
->set('guestName', 'John Doe')
|
|
->set('guestEmail', 'john@example.com')
|
|
->set('guestPhone', '+970599123456')
|
|
->set('problemSummary', 'I need legal advice regarding a contract dispute with my employer.')
|
|
->set('captchaAnswer', session('captcha_answer'))
|
|
->call('showConfirm')
|
|
->assertSet('showConfirmation', true)
|
|
->call('submit')
|
|
->assertRedirect(route('booking.success'));
|
|
|
|
$this->assertDatabaseHas('consultations', [
|
|
'guest_email' => 'john@example.com',
|
|
'guest_name' => 'John Doe',
|
|
'user_id' => null,
|
|
'status' => ConsultationStatus::Pending->value,
|
|
]);
|
|
});
|
|
|
|
test('guest cannot book twice on same day', function () {
|
|
$date = now()->addDay()->format('Y-m-d');
|
|
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
// Create existing booking for this email
|
|
Consultation::factory()->guest()->create([
|
|
'guest_email' => 'john@example.com',
|
|
'booking_date' => $date,
|
|
'status' => ConsultationStatus::Pending,
|
|
]);
|
|
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', $date, '10:00')
|
|
->set('guestName', 'John Doe')
|
|
->set('guestEmail', 'john@example.com')
|
|
->set('guestPhone', '+970599123456')
|
|
->set('problemSummary', 'Another consultation request for testing purposes.')
|
|
->set('captchaAnswer', session('captcha_answer'))
|
|
->call('showConfirm')
|
|
->assertHasErrors(['guestEmail']);
|
|
});
|
|
|
|
test('invalid captcha prevents submission', function () {
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', now()->addDay()->format('Y-m-d'), '09:00')
|
|
->set('guestName', 'John Doe')
|
|
->set('guestEmail', 'john@example.com')
|
|
->set('guestPhone', '+970599123456')
|
|
->set('problemSummary', 'I need legal advice regarding a contract dispute.')
|
|
->set('captchaAnswer', 'wrong-answer')
|
|
->call('showConfirm')
|
|
->assertHasErrors(['captchaAnswer']);
|
|
});
|
|
|
|
test('rate limiting prevents excessive booking attempts', function () {
|
|
$ipKey = 'guest-booking:127.0.0.1';
|
|
|
|
// Exhaust the rate limit (5 attempts)
|
|
for ($i = 0; $i < 5; $i++) {
|
|
RateLimiter::hit($ipKey, 60 * 60 * 24);
|
|
}
|
|
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
$date = now()->addDay()->format('Y-m-d');
|
|
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', $date, '09:00')
|
|
->set('guestName', 'John Doe')
|
|
->set('guestEmail', 'john@example.com')
|
|
->set('guestPhone', '+970599123456')
|
|
->set('problemSummary', 'I need legal advice regarding a contract dispute with my employer.')
|
|
->set('captchaAnswer', session('captcha_answer'))
|
|
->call('showConfirm')
|
|
->call('submit')
|
|
->assertHasErrors(['guestEmail']);
|
|
|
|
RateLimiter::clear($ipKey);
|
|
});
|
|
|
|
test('slot taken during submission shows error', function () {
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
$date = now()->addDay()->format('Y-m-d');
|
|
|
|
// Start the booking process
|
|
$component = Volt::test('pages.booking')
|
|
->call('selectSlot', $date, '09:00')
|
|
->set('guestName', 'John Doe')
|
|
->set('guestEmail', 'john@example.com')
|
|
->set('guestPhone', '+970599123456')
|
|
->set('problemSummary', 'I need legal advice regarding a contract dispute with my employer.')
|
|
->set('captchaAnswer', session('captcha_answer'))
|
|
->call('showConfirm')
|
|
->assertSet('showConfirmation', true);
|
|
|
|
// Simulate another booking taking the slot before submission
|
|
Consultation::factory()->guest()->create([
|
|
'booking_date' => $date,
|
|
'booking_time' => '09:00',
|
|
'status' => ConsultationStatus::Pending,
|
|
]);
|
|
|
|
// Try to submit - should fail with slot taken error
|
|
$component->call('submit')
|
|
->assertHasErrors(['selectedTime']);
|
|
});
|
|
|
|
test('guest can clear slot selection', function () {
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
$date = now()->addDay()->format('Y-m-d');
|
|
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', $date, '09:00')
|
|
->assertSet('selectedDate', $date)
|
|
->call('clearSelection')
|
|
->assertSet('selectedDate', null)
|
|
->assertSet('selectedTime', null);
|
|
});
|
|
|
|
test('guest can refresh captcha', function () {
|
|
$component = Volt::test('pages.booking');
|
|
|
|
$firstQuestion = $component->get('captchaQuestion');
|
|
|
|
$component->call('refreshCaptcha');
|
|
|
|
// Just verify the captcha answer was reset
|
|
$component->assertSet('captchaAnswer', '');
|
|
});
|
|
|
|
test('success page is accessible after booking', function () {
|
|
$this->withSession(['success' => 'Test success message'])
|
|
->get(route('booking.success'))
|
|
->assertOk()
|
|
->assertSee(__('booking.success_title'));
|
|
});
|
|
|
|
test('form validation requires all fields', function () {
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', now()->addDay()->format('Y-m-d'), '09:00')
|
|
->set('guestName', '')
|
|
->set('guestEmail', '')
|
|
->set('guestPhone', '')
|
|
->set('problemSummary', '')
|
|
->set('captchaAnswer', '')
|
|
->call('showConfirm')
|
|
->assertHasErrors(['guestName', 'guestEmail', 'guestPhone', 'problemSummary', 'captchaAnswer']);
|
|
});
|
|
|
|
test('guest name must be at least 3 characters', function () {
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', now()->addDay()->format('Y-m-d'), '09:00')
|
|
->set('guestName', 'AB')
|
|
->set('guestEmail', 'test@example.com')
|
|
->set('guestPhone', '+970599123456')
|
|
->set('problemSummary', 'This is a valid problem summary for testing.')
|
|
->set('captchaAnswer', session('captcha_answer'))
|
|
->call('showConfirm')
|
|
->assertHasErrors(['guestName']);
|
|
});
|
|
|
|
test('problem summary must be at least 20 characters', function () {
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '17:00',
|
|
]);
|
|
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', now()->addDay()->format('Y-m-d'), '09:00')
|
|
->set('guestName', 'John Doe')
|
|
->set('guestEmail', 'test@example.com')
|
|
->set('guestPhone', '+970599123456')
|
|
->set('problemSummary', 'Too short')
|
|
->set('captchaAnswer', session('captcha_answer'))
|
|
->call('showConfirm')
|
|
->assertHasErrors(['problemSummary']);
|
|
});
|
|
|
|
test('slot no longer available error is shown', function () {
|
|
// Create a working hour but no available slots
|
|
WorkingHour::factory()->create([
|
|
'day_of_week' => now()->addDay()->dayOfWeek,
|
|
'is_active' => true,
|
|
'start_time' => '09:00',
|
|
'end_time' => '10:00',
|
|
]);
|
|
|
|
$date = now()->addDay()->format('Y-m-d');
|
|
|
|
// Book the only available slot
|
|
Consultation::factory()->guest()->create([
|
|
'booking_date' => $date,
|
|
'booking_time' => '09:00',
|
|
'status' => ConsultationStatus::Approved,
|
|
]);
|
|
|
|
// Try to book the same slot
|
|
Volt::test('pages.booking')
|
|
->call('selectSlot', $date, '09:00')
|
|
->set('guestName', 'John Doe')
|
|
->set('guestEmail', 'different@example.com')
|
|
->set('guestPhone', '+970599123456')
|
|
->set('problemSummary', 'I need legal advice regarding a contract dispute.')
|
|
->set('captchaAnswer', session('captcha_answer'))
|
|
->call('showConfirm')
|
|
->assertHasErrors(['selectedTime']);
|
|
});
|