Files
libra/tests/Feature/Design/HomeBookingCardTest.php
T

159 lines
6.5 KiB
PHP

<?php
use App\Enums\ConsultationStatus;
use App\Models\Consultation;
use App\Models\User;
use App\Models\WorkingHour;
use App\Services\AvailabilityService;
use Carbon\Carbon;
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');
});
function openEveryDay(): void
{
foreach (range(0, 6) as $dow) {
WorkingHour::factory()->create(['day_of_week' => $dow, 'is_active' => true, 'start_time' => '09:00', 'end_time' => '13:00']);
}
}
function firstBookableDate(): string
{
$service = app(AvailabilityService::class);
for ($i = 0; $i < 60; $i++) {
$d = today()->addDays($i);
if (in_array($service->getDateStatus($d), ['available', 'partial'])) {
return $d->format('Y-m-d');
}
}
throw new RuntimeException('no bookable day');
}
function fillCompactForm($component, string $date, string $time = '09:00')
{
return $component
->call('selectSlot', $date, $time)
->set('guestName', 'Test Guest')
->set('guestPhone', '+970599000000')
->set('guestEmail', 'guest@example.com')
->set('problemSummary', 'A sufficiently long description of my case.')
->set('captchaAnswer', session('captcha_answer'));
}
test('strip variant lists up to 14 bookable days, selects the first and loads its slots', function () {
openEveryDay();
$first = firstBookableDate();
$component = Volt::test('availability-calendar', ['variant' => 'strip'])
->assertSet('selectedDate', $first)
->assertSee('class="days"', false)
->assertSee('class="slots"', false)
->assertDontSee('calendar-grid', false);
expect(substr_count($component->html(), 'class="day"'))->toBeGreaterThan(0)->toBeLessThanOrEqual(14);
expect($component->get('availableSlots'))->not->toBeEmpty();
$second = collect(range(1, 59))->map(fn ($i) => today()->addDays($i)->format('Y-m-d'))
->first(fn ($d) => $d > $first && in_array(app(AvailabilityService::class)->getDateStatus(Carbon::parse($d)), ['available', 'partial']));
$component->call('selectDate', $second)->assertSet('selectedDate', $second);
});
test('strip variant shows the none message when nothing is bookable', function () {
Volt::test('availability-calendar', ['variant' => 'strip'])->assertSee(__('site.booking_none'));
});
test('default variant is still the month calendar', function () {
Volt::test('availability-calendar')->assertSet('variant', 'month')->assertSee('calendar-grid', false)->assertDontSee('class="days"', false);
});
test('compact booking renders the form in the specified order', function () {
openEveryDay();
$html = Volt::test('pages.booking', ['compact' => true])->html();
$order = ['wire:model="guestName"', 'wire:model="guestPhone"', 'wire:model="guestEmail"', 'class="days"', 'class="slots"',
'wire:model="problemSummary"', 'wire:model="captchaAnswer"', 'btn btn-gold', 'class="note"'];
$last = -1;
foreach ($order as $needle) {
$pos = strpos($html, $needle);
expect($pos)->not->toBeFalse("missing $needle");
expect($pos)->toBeGreaterThan($last);
$last = $pos;
}
expect($html)->toContain('book-r');
});
test('a guest can book from the compact card and lands on success', function () {
openEveryDay();
$date = firstBookableDate();
$c = Volt::test('pages.booking', ['compact' => true]);
fillCompactForm($c, $date)->call('showConfirm')->assertHasNoErrors()->assertSet('showConfirmation', true)
->call('submit')->assertRedirect(route('booking.success'));
expect(Consultation::count())->toBe(1);
$row = Consultation::first();
expect($row->guest_email)->toBe('guest@example.com')->and($row->status)->toBe(ConsultationStatus::Pending)->and($row->user_id)->toBeNull();
});
test('compact booking keeps the same validation as the page', function () {
openEveryDay();
$date = firstBookableDate();
$c = Volt::test('pages.booking', ['compact' => true]);
fillCompactForm($c, $date)->set('problemSummary', 'too short')->call('showConfirm')->assertHasErrors(['problemSummary']);
$c = Volt::test('pages.booking', ['compact' => true]);
fillCompactForm($c, $date)->set('captchaAnswer', 'wrong')->call('showConfirm')->assertHasErrors(['captchaAnswer']);
$c = Volt::test('pages.booking', ['compact' => true])
->set('guestName', 'Test Guest')->set('guestPhone', '1')->set('guestEmail', 'a@b.co')
->set('problemSummary', 'A sufficiently long description of my case.')->set('captchaAnswer', session('captcha_answer'))
->call('showConfirm')->assertHasErrors(['selectedTime']);
expect(Consultation::count())->toBe(0);
});
test('one booking per email per day is enforced in compact mode', function () {
openEveryDay();
$date = firstBookableDate();
Consultation::factory()->create(['guest_name' => 'Other', 'guest_phone' => '123', 'guest_email' => 'guest@example.com', 'booking_date' => $date, 'booking_time' => '12:00', 'status' => ConsultationStatus::Pending, 'user_id' => null]);
$c = Volt::test('pages.booking', ['compact' => true]);
fillCompactForm($c, $date)->call('showConfirm')->assertHasErrors(['guestEmail']);
});
test('logged in clients see the message instead of the compact form', function () {
$user = User::factory()->client()->create();
Volt::actingAs($user)->test('pages.booking', ['compact' => true])
->assertSee(__('booking.logged_in_title'))
->assertSee(route('client.consultations.book'), false)
->assertDontSee('wire:model="guestName"', false);
});
test('home page has the booking card with the finale balance', function () {
$html = $this->get('/')->assertOk()->getContent();
expect($html)->toContain('id="book"')->toContain('class="book"')->toContain('book-l on-dark')
->toContain('data-balance="finale"')->toContain('tel:+970599353502')->toContain(__('footer.email'))->toContain('book-r');
});
test('the booking page itself is not compact', function () {
$html = $this->get('/booking')->assertOk()->getContent();
expect($html)->not->toContain('book-r')->toContain('calendar-grid');
});
test('booking card keys exist in both languages', function () {
foreach (['ar', 'en'] as $locale) {
$t = require base_path("lang/$locale/site.php");
foreach (['art_booking', 'booking_title', 'booking_lede', 'booking_day', 'booking_time', 'booking_submit', 'booking_none'] as $k) {
expect($t)->toHaveKey($k);
}
}
});