seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $replacementText = 'Diese Meldung wurde redaktionell entfernt.'; AdminPreset::factory()->create([ 'key' => AdminPreset::PRESS_RELEASE_DELETED_PUBLISHED_TEXT, 'area' => 'press_releases', 'type' => 'text', 'label' => 'Ersatztext', 'value' => $replacementText, ]); $pressRelease = PressRelease::factory() ->published() ->create(['text' => 'Alter veröffentlichter Inhalt.']); LivewireVolt::test('admin.press-releases.edit', ['id' => $pressRelease->id]) ->call('deletePressRelease') ->assertRedirect(route('admin.press-releases.index')); $pressRelease->refresh(); expect($pressRelease->deleted_at)->toBeNull() ->and($pressRelease->status)->toBe(PressReleaseStatus::Archived) ->and($pressRelease->text)->toBe($replacementText) ->and($pressRelease->no_export)->toBeTrue(); }); test('unpublished press releases are soft deleted by admin delete action', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $pressRelease = PressRelease::factory()->create([ 'status' => PressReleaseStatus::Draft->value, ]); LivewireVolt::test('admin.press-releases.edit', ['id' => $pressRelease->id]) ->call('deletePressRelease') ->assertRedirect(route('admin.press-releases.index')); $this->assertSoftDeleted($pressRelease); }); test('edit page renders status selector and confirmation modal', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $pressRelease = PressRelease::factory() ->inReview() ->create(); LivewireVolt::test('admin.press-releases.edit', ['id' => $pressRelease->id]) ->assertSee('Neuer Status') ->assertSee('Status wechseln') ->assertSee('Status wirklich wechseln?') ->assertSee('Pressemitteilung löschen?'); }); test('admin can change archived press release back to another status', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $pressRelease = PressRelease::factory()->create([ 'status' => PressReleaseStatus::Archived->value, ]); LivewireVolt::test('admin.press-releases.edit', ['id' => $pressRelease->id]) ->set('targetStatus', PressReleaseStatus::Draft->value) ->call('changeStatus') ->assertHasNoErrors(); $pressRelease->refresh(); expect($pressRelease->status)->toBe(PressReleaseStatus::Draft); }); test('press release index renders confirmation modals for status actions', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); PressRelease::factory() ->inReview() ->create(['title' => 'PM in Prüfung']); PressRelease::factory() ->published() ->create(['title' => 'Veröffentlichte PM']); LivewireVolt::test('admin.press-releases.index') ->assertSee('Pressemitteilung veröffentlichen?') ->assertSee('Pressemitteilung ablehnen?') ->assertSee('Pressemitteilung archivieren?'); }); test('ai check modal shows classification details from the latest audit', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $pressRelease = PressRelease::factory() ->inReview() ->create(['classification' => PressReleaseClassification::Yellow->value]); KiAudit::factory() ->classification(PressReleaseClassification::Yellow) ->create([ 'press_release_id' => $pressRelease->id, 'reason' => 'Werbliche Sprache im Titel gefunden.', 'provider' => 'anthropic', ]); LivewireVolt::test('admin.press-releases.index') ->call('openAiCheck', $pressRelease->id) ->assertSet('aiCheckId', $pressRelease->id) ->assertSee('Werbliche Sprache im Titel gefunden.') ->assertSee('anthropic') ->assertSee('Erneut prüfen'); }); test('ai check modal shows empty state and start button for unchecked press releases', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $pressRelease = PressRelease::factory() ->inReview() ->create(['classification' => null]); LivewireVolt::test('admin.press-releases.index') ->call('openAiCheck', $pressRelease->id) ->assertSee('Noch nicht geprüft') ->assertSee('Noch nicht bewertet') ->assertSee('Prüfung starten'); }); test('ai check modal shows the content score breakdown from the audit raw response', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $pressRelease = PressRelease::factory() ->inReview() ->create([ 'classification' => PressReleaseClassification::Green->value, 'content_score' => 62, 'content_tier' => PressReleaseContentTier::Geprueft->value, ]); KiAudit::factory() ->contentScore(62) ->create([ 'press_release_id' => $pressRelease->id, 'provider' => 'openai', 'raw_response' => [ 'breakdown' => [ 'pressestil' => 12, 'struktur' => 8, 'bild' => 0, ], ], ]); LivewireVolt::test('admin.press-releases.index') ->call('openAiCheck', $pressRelease->id) ->assertSee('Pressestil') ->assertSee('12 / 25') ->assertSee('Struktur') ->assertSee('8 / 15') ->assertSee('Bildmaterial') ->assertSee('0 / 10'); }); test('ai check modal explains missing reason for green classifications', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $pressRelease = PressRelease::factory() ->inReview() ->create(['classification' => PressReleaseClassification::Green->value]); KiAudit::factory() ->classification(PressReleaseClassification::Green) ->create([ 'press_release_id' => $pressRelease->id, 'reason' => null, ]); LivewireVolt::test('admin.press-releases.index') ->call('openAiCheck', $pressRelease->id) ->assertSee('bei Grün liefert die KI keine Begründung'); }); test('runAiCheck from modal dispatches jobs without routing and marks the run as pending', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); Queue::fake(); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $pressRelease = PressRelease::factory() ->inReview() ->create(['classification' => null]); LivewireVolt::test('admin.press-releases.index') ->call('openAiCheck', $pressRelease->id) ->assertSet('aiCheckStartedAt', null) ->call('runAiCheck') ->assertSee('Prüfung läuft'); Queue::assertPushed(ClassifyPressRelease::class, fn (ClassifyPressRelease $job): bool => $job->pressReleaseId === $pressRelease->id && $job->route === false); Queue::assertPushed(ScorePressRelease::class, fn (ScorePressRelease $job): bool => $job->pressReleaseId === $pressRelease->id); }); test('show page renders confirmation modals for status actions', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $this->actingAs($admin); $reviewPressRelease = PressRelease::factory() ->inReview() ->create(); LivewireVolt::test('admin.press-releases.show', ['id' => $reviewPressRelease->id]) ->assertSee('Pressemitteilung veröffentlichen?') ->assertSee('Pressemitteilung ablehnen?'); $publishedPressRelease = PressRelease::factory() ->published() ->create(); LivewireVolt::test('admin.press-releases.show', ['id' => $publishedPressRelease->id]) ->assertSee('Pressemitteilung archivieren?'); });