Compare commits
3 commits
7f7a9450bf
...
c1d8008b5d
| Author | SHA1 | Date | |
|---|---|---|---|
| c1d8008b5d | |||
| 95c5a72294 | |||
| c3a5532028 |
7 changed files with 115 additions and 28 deletions
|
|
@ -1662,12 +1662,6 @@ parameters:
|
||||||
count: 2
|
count: 2
|
||||||
path: tests/Unit/Schedule/ScheduleGeneratorTest.php
|
path: tests/Unit/Schedule/ScheduleGeneratorTest.php
|
||||||
|
|
||||||
-
|
|
||||||
message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<int,Carbon\\Carbon\>\:\:reduce\(\) expects callable\(Illuminate\\Support\\Carbon\|null, Carbon\\Carbon, int\)\: Illuminate\\Support\\Carbon, Closure\(Illuminate\\Support\\Carbon\|null, Illuminate\\Support\\Carbon\)\: Illuminate\\Support\\Carbon given\.$#'
|
|
||||||
identifier: argument.type
|
|
||||||
count: 1
|
|
||||||
path: tests/Unit/Schedule/ScheduleGeneratorTest.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#'
|
message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#'
|
||||||
identifier: property.notFound
|
identifier: property.notFound
|
||||||
|
|
|
||||||
30
shell.nix
30
shell.nix
|
|
@ -109,7 +109,35 @@ pkgs.mkShell {
|
||||||
pest-browser() {
|
pest-browser() {
|
||||||
# Run Pest browser tests (Playwright) on the host. Pass --headed for a
|
# Run Pest browser tests (Playwright) on the host. Pass --headed for a
|
||||||
# visible browser or --debug to pause on failure. Requires `npm run build`.
|
# visible browser or --debug to pause on failure. Requires `npm run build`.
|
||||||
vendor/bin/pest tests/Browser "$@"
|
# A path argument replaces the default suite; flags alone keep it.
|
||||||
|
# Only existing paths count, so `--filter Logout` cannot silently
|
||||||
|
# unscope the run to the whole test directory.
|
||||||
|
local has_path=0
|
||||||
|
local skip_next=0
|
||||||
|
local arg
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [ "$skip_next" -eq 1 ]; then
|
||||||
|
skip_next=0
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
case "$arg" in
|
||||||
|
--filter|--group|--exclude-group|--test-suffix)
|
||||||
|
skip_next=1
|
||||||
|
;;
|
||||||
|
-*) ;;
|
||||||
|
*)
|
||||||
|
if [ -e "$arg" ]; then
|
||||||
|
has_path=1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$has_path" -eq 1 ]; then
|
||||||
|
vendor/bin/pest "$@"
|
||||||
|
else
|
||||||
|
vendor/bin/pest tests/Browser "$@"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
dev-fix-permissions() {
|
dev-fix-permissions() {
|
||||||
|
|
|
||||||
31
tests/Browser/Auth/LogoutTest.php
Normal file
31
tests/Browser/Auth/LogoutTest.php
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
it('logs out and lands on the login page', function () {
|
||||||
|
$page = loginAs(createPlanner());
|
||||||
|
|
||||||
|
logout($page)
|
||||||
|
->assertPathIs('/login')
|
||||||
|
->assertSee('Sign In');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('blocks protected routes after logging out', function (string $path) {
|
||||||
|
$page = loginAs(createPlanner());
|
||||||
|
|
||||||
|
logout($page)
|
||||||
|
->assertPathIs('/login')
|
||||||
|
->navigate($path)
|
||||||
|
->assertPathIs('/login')
|
||||||
|
->assertSee('Sign In');
|
||||||
|
})->with(['/dashboard', '/dishes', '/schedule', '/users']);
|
||||||
|
|
||||||
|
it('does not restore an authenticated view via the back button', function () {
|
||||||
|
$planner = createPlanner();
|
||||||
|
|
||||||
|
$page = loginAs($planner)->assertSee($planner->name);
|
||||||
|
|
||||||
|
logout($page)
|
||||||
|
->assertPathIs('/login')
|
||||||
|
->back()
|
||||||
|
->assertPathIs('/login')
|
||||||
|
->assertDontSee('Logout');
|
||||||
|
});
|
||||||
|
|
@ -90,4 +90,21 @@ public function test_users_can_logout(): void
|
||||||
$response->assertRedirect('/');
|
$response->assertRedirect('/');
|
||||||
$this->assertGuest();
|
$this->assertGuest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_logout_invalidates_the_session(): void
|
||||||
|
{
|
||||||
|
$user = Planner::factory()->create();
|
||||||
|
|
||||||
|
// Boots the session store so getId() below reflects a real value.
|
||||||
|
$this->actingAs($user)->get('/dashboard');
|
||||||
|
|
||||||
|
session()->put('scratch', 'value');
|
||||||
|
|
||||||
|
$sessionId = session()->getId();
|
||||||
|
|
||||||
|
$this->actingAs($user)->post('/logout');
|
||||||
|
|
||||||
|
$this->assertNotSame($sessionId, session()->getId());
|
||||||
|
$this->assertFalse(session()->has('scratch'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
use App\Models\Planner;
|
use App\Models\Planner;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Pest\Browser\Api\AwaitableWebpage;
|
||||||
use Pest\Browser\Api\Webpage;
|
use Pest\Browser\Api\Webpage;
|
||||||
use Tests\BrowserTestCase;
|
use Tests\BrowserTestCase;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
@ -62,3 +63,15 @@ function loginAndGoToSchedule()
|
||||||
{
|
{
|
||||||
return loginAs(createPlanner(), '/schedule');
|
return loginAs(createPlanner(), '/schedule');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the account dropdown and submit the logout form.
|
||||||
|
*
|
||||||
|
* @return Webpage
|
||||||
|
*/
|
||||||
|
function logout(Webpage|AwaitableWebpage $page)
|
||||||
|
{
|
||||||
|
return $page
|
||||||
|
->click('button[\@click="open = !open"]')
|
||||||
|
->click('.sm\:flex form[action$="/logout"] button[type="submit"]');
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
use App\WeekdaysEnum;
|
use App\WeekdaysEnum;
|
||||||
use DishPlanner\Schedule\Services\ScheduleGenerator;
|
use DishPlanner\Schedule\Services\ScheduleGenerator;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
use Tests\Traits\HasPlanner;
|
use Tests\Traits\HasPlanner;
|
||||||
|
|
||||||
|
|
@ -128,19 +127,23 @@ public function test_it_takes_minimum_recurrences_into_account(): void
|
||||||
|
|
||||||
$this->assertTrue(Schedule::all()->isNotEmpty());
|
$this->assertTrue(Schedule::all()->isNotEmpty());
|
||||||
|
|
||||||
Schedule::all()
|
$recurringDates = Schedule::all()
|
||||||
->filter(fn (Schedule $schedule) => $schedule->scheduledUserDishes()->first()->userDish->dish->id === $dishRecurring->id)
|
->filter(fn (Schedule $schedule) => $schedule->scheduledUserDishes
|
||||||
|
->contains(fn ($scheduledUserDish) => $scheduledUserDish->userDish->dish->id === $dishRecurring->id)
|
||||||
|
)
|
||||||
->map(fn (Schedule $schedule) => $schedule->date)
|
->map(fn (Schedule $schedule) => $schedule->date)
|
||||||
->reduce(function (?Carbon $previousDate, Carbon $currentDate) use ($recurringMinimum) {
|
->sort()
|
||||||
if (! is_null($previousDate)) {
|
->values();
|
||||||
$this->assertGreaterThanOrEqual(
|
|
||||||
$recurringMinimum,
|
|
||||||
$previousDate->diffInDays($currentDate),
|
|
||||||
'Dates are not spaced properly'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $currentDate;
|
$this->assertGreaterThan(1, $recurringDates->count(), 'Recurring dish was not scheduled often enough to verify spacing');
|
||||||
});
|
|
||||||
|
$gaps = $recurringDates
|
||||||
|
->sliding(2)
|
||||||
|
->map(fn ($pair) => (int) $pair->first()->diffInDays($pair->last()));
|
||||||
|
|
||||||
|
$this->assertEmpty(
|
||||||
|
$gaps->reject(fn (int $gap) => $gap >= $recurringMinimum)->all(),
|
||||||
|
'Dates are not spaced properly'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,19 +55,20 @@ public function test_includes_correct_day_numbers(): void
|
||||||
|
|
||||||
public function test_marks_today_correctly(): void
|
public function test_marks_today_correctly(): void
|
||||||
{
|
{
|
||||||
|
$this->travelTo(Carbon::createFromDate(2026, 3, 15)->startOfDay());
|
||||||
|
|
||||||
$planner = $this->planner;
|
$planner = $this->planner;
|
||||||
$today = now();
|
|
||||||
|
|
||||||
$calendarDays = $this->service->getCalendarDays($planner, $today->month, $today->year);
|
$calendarDays = $this->service->getCalendarDays($planner, 3, 2026);
|
||||||
|
|
||||||
$todayIndex = $today->day - 1;
|
$todayIndex = 14;
|
||||||
$this->assertTrue($calendarDays[$todayIndex]['isToday']);
|
$this->assertTrue($calendarDays[$todayIndex]['isToday']);
|
||||||
|
|
||||||
foreach ($calendarDays as $index => $day) {
|
$otherDaysMarkedToday = collect($calendarDays)
|
||||||
if ($index !== $todayIndex && $day['day'] !== null) {
|
->filter(fn (array $day, int $index) => $index !== $todayIndex && $day['day'] !== null)
|
||||||
$this->assertFalse($day['isToday']);
|
->filter(fn (array $day) => $day['isToday']);
|
||||||
}
|
|
||||||
}
|
$this->assertCount(0, $otherDaysMarkedToday);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_includes_scheduled_dishes(): void
|
public function test_includes_scheduled_dishes(): void
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue