lernschiff/tests/Feature/Livewire/UrlStateTest.php

130 lines
4.7 KiB
PHP

<?php
use App\Livewire\Notifications\Index as NotificationsIndex;
use App\Livewire\Tasks\Index as TasksIndex;
use App\Models\License;
use App\Models\Role;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Livewire\Livewire;
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
$this->seed(\Database\Seeders\RolesSeeder::class);
$tenant = Tenant::firstOrCreate(
['name' => 'Test Schule'],
['type' => 'school', 'active' => true]
);
$this->user = User::withoutGlobalScopes()->create([
'name' => 'URL Tester', 'email' => 'url@test.example',
'tenant_id' => $tenant->id, 'password' => bcrypt('password'),
]);
$role = Role::where('name', 'child')->first();
DB::table('role_user')->insert([
'user_id' => $this->user->id, 'role_id' => $role->id,
'created_at' => now(), 'updated_at' => now(),
]);
$license = License::withoutGlobalScopes()->create([
'tenant_id' => $tenant->id, 'type' => 'school_flat',
'expires_at' => now()->addYear(), 'active' => true,
]);
DB::table('license_assignments')->insert([
'license_id' => $license->id, 'user_id' => $this->user->id,
'created_at' => now(), 'updated_at' => now(),
]);
$this->actingAs($this->user);
});
it('Tasks: default filter is open, server-renders correct from first paint', function () {
Livewire::test(TasksIndex::class)
->assertSet('filter', 'open')
->assertSee('Multiplikation bis 100')
->assertDontSee('Gedicht lernen');
});
it('Tasks: filter param from URL hydrates initial state', function () {
Livewire::withQueryParams(['filter' => 'done'])
->test(TasksIndex::class)
->assertSet('filter', 'done')
->assertSee('Gedicht lernen')
->assertDontSee('Multiplikation bis 100');
});
it('Tasks: invalid filter param falls back to open', function () {
Livewire::withQueryParams(['filter' => 'evil-injection'])
->test(TasksIndex::class)
->assertSet('filter', 'open');
});
it('Tasks: setFilter only accepts allowed values', function () {
Livewire::test(TasksIndex::class)
->call('setFilter', 'done')
->assertSet('filter', 'done')
->call('setFilter', 'all')
->assertSet('filter', 'all')
->call('setFilter', 'malicious')
->assertSet('filter', 'all');
});
it('Tasks: filter pill has aria-current=page when active', function () {
Livewire::test(TasksIndex::class)
->call('setFilter', 'done')
->assertSeeHtml('data-testid="filter-done"')
->assertSeeHtml('aria-current="page"');
});
it('Tasks: skeleton uses bg-bg-soft (same as table card), with bg-line bars', function () {
$html = Livewire::test(TasksIndex::class)->html();
expect($html)->toContain('wire:loading.flex');
expect($html)->toContain('animate-pulse');
expect($html)->toContain('bg-line');
expect($html)->toContain('data-testid="tasks-skeleton"');
// Skeleton container must use same surface as table card (bg-bg-soft within same tag)
expect(preg_match('/<div[^>]+class="[^"]*\bbg-bg-soft\b[^"]*"[^>]+data-testid="tasks-skeleton"/', $html))->toBe(1);
});
it('Tasks: task row is NOT clickable; modal trigger is button only', function () {
$html = Livewire::test(TasksIndex::class)->html();
// Detail-button is the wire:click trigger
expect($html)->toContain('data-testid="task-detail-btn"');
expect($html)->toContain("openModal");
// Row container does NOT have wire:click on itself
expect(preg_match('/data-testid="task-row"[^>]*wire:click=/', $html))->toBe(0);
});
it('Notifications: default filter is all', function () {
Livewire::test(NotificationsIndex::class)->assertSet('filter', 'all');
});
it('Notifications: filter param "unread" hydrates from URL', function () {
Livewire::withQueryParams(['filter' => 'unread'])
->test(NotificationsIndex::class)
->assertSet('filter', 'unread');
});
it('Notifications: invalid filter falls back to all', function () {
Livewire::withQueryParams(['filter' => 'bogus'])
->test(NotificationsIndex::class)
->assertSet('filter', 'all');
});
it('Notifications: markRead adds key', function () {
Livewire::test(NotificationsIndex::class)
->call('markRead', 'n1')
->assertSet('readKeys', ['n1']);
});
it('Notifications: skeleton uses bg-bg-soft + bg-line bars', function () {
$html = Livewire::test(NotificationsIndex::class)->html();
expect($html)->toContain('data-testid="notif-skeleton"');
expect($html)->toContain('bg-line');
expect(preg_match('/<div[^>]+class="[^"]*\bbg-bg-soft\b[^"]*"[^>]+data-testid="notif-skeleton"/', $html))->toBe(1);
});