guest()->pending()->create(); Mail::to($consultation->guest_email)->queue( new GuestBookingSubmittedMail($consultation) ); Mail::assertQueued(GuestBookingSubmittedMail::class, function ($mail) use ($consultation) { return $mail->hasTo($consultation->guest_email); }); }); test('guest receives approval email with calendar attachment', function () { Mail::fake(); $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Approved, 'consultation_type' => ConsultationType::Free, ]); Mail::to($consultation->guest_email)->queue( new GuestBookingApprovedMail($consultation, emailLocale: 'en') ); Mail::assertQueued(GuestBookingApprovedMail::class, function ($mail) use ($consultation) { return $mail->hasTo($consultation->guest_email); }); }); test('guest booking approved mail has calendar attachment', function () { $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Approved, 'consultation_type' => ConsultationType::Free, ]); $mail = new GuestBookingApprovedMail($consultation, emailLocale: 'en'); $attachments = $mail->attachments(); expect($attachments)->toHaveCount(1); }); test('guest receives rejection email', function () { Mail::fake(); $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Rejected, ]); Mail::to($consultation->guest_email)->queue( new GuestBookingRejectedMail($consultation, emailLocale: 'en', reason: 'Not available') ); Mail::assertQueued(GuestBookingRejectedMail::class, function ($mail) use ($consultation) { return $mail->hasTo($consultation->guest_email); }); }); test('guest booking rejected mail includes reason when provided', function () { $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Rejected, ]); $mail = new GuestBookingRejectedMail($consultation, emailLocale: 'en', reason: 'Schedule conflict'); $content = $mail->content(); expect($content->with['hasReason'])->toBeTrue(); expect($content->with['reason'])->toBe('Schedule conflict'); }); test('guest booking rejected mail handles missing reason', function () { $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Rejected, ]); $mail = new GuestBookingRejectedMail($consultation, emailLocale: 'en', reason: null); $content = $mail->content(); expect($content->with['hasReason'])->toBeFalse(); expect($content->with['reason'])->toBeNull(); }); test('admin email shows guest indicator for guest bookings', function () { $consultation = Consultation::factory()->guest()->pending()->create(); $mail = new NewBookingAdminEmail($consultation); $content = $mail->content(); expect($content->with['isGuest'])->toBeTrue(); expect($content->with['clientName'])->toBe($consultation->guest_name); expect($content->with['clientEmail'])->toBe($consultation->guest_email); expect($content->with['clientPhone'])->toBe($consultation->guest_phone); }); test('admin email shows client info for client bookings', function () { $client = User::factory()->individual()->create(); $consultation = Consultation::factory()->pending()->create([ 'user_id' => $client->id, ]); $mail = new NewBookingAdminEmail($consultation); $content = $mail->content(); expect($content->with['isGuest'])->toBeFalse(); expect($content->with['clientName'])->toBe($client->full_name); expect($content->with['clientEmail'])->toBe($client->email); expect($content->with['clientPhone'])->toBe($client->phone); }); test('guest booking approved mail uses correct arabic locale', function () { $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Approved, 'consultation_type' => ConsultationType::Free, ]); $mail = new GuestBookingApprovedMail($consultation, emailLocale: 'ar'); $envelope = $mail->envelope(); $content = $mail->content(); expect($envelope->subject)->toContain('تأكيد الحجز'); expect($content->markdown)->toBe('emails.booking.guest-approved.ar'); }); test('guest booking approved mail uses correct english locale', function () { $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Approved, 'consultation_type' => ConsultationType::Free, ]); $mail = new GuestBookingApprovedMail($consultation, emailLocale: 'en'); $envelope = $mail->envelope(); $content = $mail->content(); expect($envelope->subject)->toContain('Booking Confirmed'); expect($content->markdown)->toBe('emails.booking.guest-approved.en'); }); test('guest booking rejected mail uses correct arabic locale', function () { $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Rejected, ]); $mail = new GuestBookingRejectedMail($consultation, emailLocale: 'ar', reason: null); $envelope = $mail->envelope(); $content = $mail->content(); expect($envelope->subject)->toContain('تحديث الحجز'); expect($content->markdown)->toBe('emails.booking.guest-rejected.ar'); }); test('guest booking approved mail includes payment info for paid consultations', function () { $consultation = Consultation::factory()->guest()->create([ 'status' => ConsultationStatus::Approved, 'consultation_type' => ConsultationType::Paid, 'payment_amount' => 150.00, ]); $mail = new GuestBookingApprovedMail($consultation, emailLocale: 'en', paymentInstructions: 'Bank transfer details'); $content = $mail->content(); expect($content->with['isPaid'])->toBeTrue(); expect($content->with['paymentAmount'])->toBe('150.00'); expect($content->with['paymentInstructions'])->toBe('Bank transfer details'); });