seed(\Database\Seeders\RolesSeeder::class); $tenant = Tenant::firstOrCreate( ['name' => 'Test Schule'], ['type' => 'school', 'active' => true] ); $this->user = User::withoutGlobalScopes()->create([ 'name' => 'Modal Tester', 'email' => 'modal@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('Subjects\\Modals\\Detail extends ModalComponent and renders', function () { expect(is_subclass_of(SubjectDetail::class, ModalComponent::class))->toBeTrue(); Livewire::test(SubjectDetail::class, [ 'subject' => ['name' => 'Mathematik', 'icon' => '∑', 'color' => 'primary', 'progress' => 75, 'lessons' => '18 / 24', 'next' => 'Multiplizieren', 'level' => 'K4'], ]) ->assertSee('Mathematik') ->assertSee('75 %') ->assertSee('Multiplizieren'); }); it('Tasks\\Modals\\Detail renders with task data', function () { Livewire::test(TaskDetail::class, [ 'task' => ['title' => 'Multiplikation', 'meta' => 'Mathe · 12 Aufgaben', 'points' => 120, 'due' => 'Heute', 'status' => 'open'], ]) ->assertSee('Multiplikation') ->assertSee('+120') ->assertSee('Heute') ->assertSee('Offen'); }); it('PhotoHelp\\Modals\\Result renders step-by-step solution', function () { Livewire::test(PhotoResult::class) ->assertSee('Erkennung') ->assertSee('Methode') ->assertSee('Schritte'); }); it('Rewards\\Modals\\Purchase: confirm + purchase flow', function () { $item = ['title' => 'Avatar', 'emoji' => '🎨', 'category' => 'Avatar', 'desc' => 'Cool item', 'price' => 800]; Livewire::test(RewardsPurchase::class, ['item' => $item, 'balance' => 2480]) ->assertSee('Avatar') ->assertSee('800') ->call('confirm') ->assertSet('confirming', true) ->call('purchase') ->assertSet('purchased', true) ->assertSet('balance', 1680); }); it('Rewards\\Modals\\Purchase: cannot purchase if insufficient balance', function () { $item = ['title' => 'Drache', 'emoji' => '🐉', 'category' => 'Tier', 'desc' => 'Big', 'price' => 5000]; Livewire::test(RewardsPurchase::class, ['item' => $item, 'balance' => 100]) ->call('purchase') ->assertSet('purchased', false) ->assertSet('balance', 100); }); it('Notifications\\Modals\\Detail renders notification body', function () { Livewire::test(NotificationDetail::class, [ 'notification' => ['title' => 'Neue Aufgabe', 'body' => 'Heute warten 4 Aufgaben.', 'time' => 'vor 10 Min.'], ]) ->assertSee('Neue Aufgabe') ->assertSee('Heute warten 4 Aufgaben.'); }); it('Settings\\Modals\\LogoutAll renders confirm prompt', function () { Livewire::test(SettingsLogoutAll::class) ->assertSee('Auf allen Geräten abmelden'); }); it('Settings\\Modals\\Delete: only deletes when confirmText is LÖSCHEN', function () { Livewire::test(SettingsDelete::class) ->set('confirmText', 'falsch') ->call('delete') ->assertSet('confirmText', 'falsch') ->set('confirmText', 'LÖSCHEN') ->call('delete'); }); it('all modal classes extend wire-elements ModalComponent', function () { $modals = [ SubjectDetail::class, TaskDetail::class, PhotoResult::class, RewardsPurchase::class, NotificationDetail::class, SettingsLogoutAll::class, SettingsDelete::class, ]; foreach ($modals as $modal) { expect(is_subclass_of($modal, ModalComponent::class)) ->toBeTrue("{$modal} must extend LivewireUI\\Modal\\ModalComponent"); } }); it('dashboard layout includes livewire-ui-modal container', function () { $response = $this->get(route('dashboard')); $response->assertOk(); $response->assertSee('livewire-ui-modal', false); }); it('Livewire ComponentRegistry resolves feature modal aliases', function () { $registry = app('livewire'); // Each modal alias must be resolvable by Livewire to its class $cases = [ 'subjects.modals.detail' => SubjectDetail::class, 'tasks.modals.detail' => TaskDetail::class, 'photo-help.modals.result' => PhotoResult::class, 'rewards.modals.purchase' => RewardsPurchase::class, 'notifications.modals.detail' => NotificationDetail::class, 'settings.modals.logout-all' => SettingsLogoutAll::class, 'settings.modals.delete' => SettingsDelete::class, ]; foreach ($cases as $alias => $class) { $resolved = app('livewire.finder')->resolveClassComponentClassName($alias); expect($resolved)->toBe($class, "Alias [{$alias}] must resolve to {$class}, got {$resolved}"); } });