lernschiff/tests/Feature/Livewire/SettingsNavTest.php

85 lines
3.1 KiB
PHP

<?php
use App\Livewire\Settings\Index as SettingsIndex;
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' => 'Settings Nav Tester', 'email' => 'sn@test.example',
'tenant_id' => $tenant->id, 'password' => bcrypt('password'),
]);
$role = Role::where('name', 'school-admin')->first();
DB::table('role_user')->insert([
'user_id' => $this->user->id, 'role_id' => $role->id,
'created_at' => now(), 'updated_at' => now(),
]);
$this->actingAs($this->user);
});
it('default section is profile, server-rendered (no Alpine flicker)', function () {
Livewire::test(SettingsIndex::class)
->assertSet('section', 'profile')
->assertSee('Profil')
->assertSeeHtml('data-section="profile"')
->assertDontSeeHtml('data-section="language"');
});
it('?section=security URL param hydrates initial state', function () {
Livewire::withQueryParams(['section' => 'security'])
->test(SettingsIndex::class)
->assertSet('section', 'security')
->assertSeeHtml('data-section="security"');
});
it('invalid section param falls back to profile', function () {
Livewire::withQueryParams(['section' => 'evil'])
->test(SettingsIndex::class)
->assertSet('section', 'profile');
});
it('setSection only accepts allowed values', function () {
Livewire::test(SettingsIndex::class)
->call('setSection', 'security')
->assertSet('section', 'security')
->call('setSection', 'malicious')
->assertSet('section', 'security');
});
it('only one section card renders at a time (no flicker)', function () {
$html = Livewire::test(SettingsIndex::class)->html();
// Profile renders, others do NOT (server-side conditional)
expect($html)->toContain('data-section="profile"');
expect($html)->not->toContain('data-section="language"');
expect($html)->not->toContain('data-section="security"');
expect($html)->not->toContain('data-section="notifications"');
expect($html)->not->toContain('data-section="danger"');
});
it('sub-nav uses server-rendered active state (aria-current)', function () {
$html = Livewire::test(SettingsIndex::class)->html();
expect($html)->toMatch('/data-testid="settings-nav-profile"[^>]*aria-current="page"|aria-current="page"[^>]*data-testid="settings-nav-profile"/');
});
it('sub-nav buttons NOT using :class Alpine bindings (no client-side flicker)', function () {
$tpl = file_get_contents(base_path('resources/views/livewire/settings/index.blade.php'));
// Sub-nav must use plain class string (server-side), not :class= for active state
expect($tpl)->not->toContain(':class="activeSection ===');
});