feat: add booking time ranges and repeating series
This commit is contained in:
1 parent
59feca2f8e
commit
d433d271d0
29 files changed
+2135
-180
No files matched your search
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Booking;
|
||||
|
||||
use App\Booking\TimeRange;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
|
||||
class TimeRangeTest extends TestCase
|
||||
{
|
||||
public function testAWholeDayRunsFromMidnightToMidnight(): void
|
||||
{
|
||||
$range = TimeRange::wholeDay();
|
||||
|
||||
self::assertTrue($range->isAllDay());
|
||||
self::assertSame(0, $range->startMinute);
|
||||
self::assertSame(1440, $range->endMinute);
|
||||
}
|
||||
|
||||
public function testTheEndMustComeAfterTheStart(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
TimeRange::fromMinutes(13 * 60, 9 * 60);
|
||||
}
|
||||
|
||||
public function testARangeCannotLeaveTheDay(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
TimeRange::fromMinutes(0, 1441);
|
||||
}
|
||||
|
||||
public function testTouchingRangesDoNotOverlap(): void
|
||||
{
|
||||
$morning = TimeRange::fromMinutes(9 * 60, 13 * 60);
|
||||
$afternoon = TimeRange::fromMinutes(13 * 60, 17 * 60);
|
||||
|
||||
// Half-open, so handing a desk over at 13:00 is not a collision.
|
||||
self::assertFalse($morning->overlaps($afternoon));
|
||||
self::assertFalse($afternoon->overlaps($morning));
|
||||
}
|
||||
|
||||
public function testPartlyCoveringRangesOverlapBothWays(): void
|
||||
{
|
||||
$a = TimeRange::fromMinutes(9 * 60, 13 * 60);
|
||||
$b = TimeRange::fromMinutes(12 * 60, 17 * 60);
|
||||
|
||||
self::assertTrue($a->overlaps($b));
|
||||
self::assertTrue($b->overlaps($a));
|
||||
}
|
||||
|
||||
public function testAWholeDayOverlapsEverything(): void
|
||||
{
|
||||
self::assertTrue(TimeRange::wholeDay()->overlaps(TimeRange::fromMinutes(0, 15)));
|
||||
}
|
||||
|
||||
#[DataProvider('urlPairs')]
|
||||
public function testParsingTheBoardsQueryParameters(?string $from, ?string $to, ?string $expected): void
|
||||
{
|
||||
$range = TimeRange::parse($from, $to);
|
||||
|
||||
self::assertSame($expected, null === $range ? null : $range->start().'-'.$range->end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string, array{?string, ?string, ?string}>
|
||||
*/
|
||||
public static function urlPairs(): iterable
|
||||
{
|
||||
yield 'both given' => ['09:00', '13:00', '09:00-13:00'];
|
||||
yield 'only a start is open to the end of the day' => ['09:00', null, '09:00-24:00'];
|
||||
yield 'only an end is open from midnight' => [null, '13:00', '00:00-13:00'];
|
||||
yield 'neither means the whole day, i.e. no filter' => [null, null, null];
|
||||
yield 'empty strings are the same as absent' => ['', '', null];
|
||||
yield 'reversed is rejected' => ['13:00', '09:00', null];
|
||||
yield 'equal is rejected' => ['09:00', '09:00', null];
|
||||
yield 'nonsense is rejected' => ['halb zehn', '13:00', null];
|
||||
yield 'out of range is rejected' => ['25:00', '26:00', null];
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression guard for a trap that cost real debugging time: Twig resolves `range.allDay` by
|
||||
* looking for a method literally named `allDay` BEFORE it tries `isAllDay()`, and it happily
|
||||
* calls a static one. While the factory was called allDay(), every template read back a
|
||||
* freshly built all-day range -- always truthy -- so the whole board silently ignored the
|
||||
* selected span. Renaming the factory to wholeDay() is what fixes it; this keeps it fixed.
|
||||
*/
|
||||
public function testTheAllDayAccessorIsNotShadowedInTwig(): void
|
||||
{
|
||||
$twig = new Environment(new ArrayLoader(['t' => '{{ range.allDay ? "yes" : "no" }}']));
|
||||
|
||||
self::assertSame('no', $twig->render('t', ['range' => TimeRange::fromMinutes(9 * 60, 13 * 60)]));
|
||||
self::assertSame('yes', $twig->render('t', ['range' => TimeRange::wholeDay()]));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user