create(['portal' => Portal::Presseecho]); $category->translations()->create(['locale' => 'de', 'name' => 'Energie', 'slug' => 'energie']); $company = Company::factory()->presseecho()->create(['name' => 'Beispiel AG', 'is_active' => true]); $release = PressRelease::factory() ->published() ->forPortal(Portal::Presseecho) ->for($category) ->for($company) ->create(['slug' => 'bild-nachweis-test', 'published_at' => now()->subDay()]); return compact('category', 'company', 'release'); } function creditConfirmationFor(PressReleaseImage $image, string $channel = 'form'): ImageRightsConfirmation { return ImageRightsConfirmation::query()->create([ 'subject_type' => $image->getMorphClass(), 'subject_id' => $image->getKey(), 'press_release_id' => $image->press_release_id, 'company_id' => $image->pressRelease->company_id, 'user_id' => $image->pressRelease->user_id, 'channel' => ConfirmationChannel::from($channel), 'legal_text_key' => 'img-confirm-v2', 'text_snapshot' => 'Ich bestätige …', 'agb_version' => '0.6', 'file_sha256' => $image->original_sha256, 'declaration_snapshot' => [], 'confirmed_at' => now()->setDate(2026, 9, 22)->setTime(10, 0), 'created_at' => now(), ]); } test('a formular v2 image renders credit line, ai hint with source and the responsible line', function () { /** @var TestCase $this */ ['release' => $release, 'company' => $company] = creditPublishedRelease(); $image = PressReleaseImage::factory()->for($release)->rulesV2()->create([ 'copyright' => 'Anna Beispiel / Beispiel AG', 'ai_declaration' => 'ai_edited', 'ai_declaration_user' => 'ai_edited', 'ai_declaration_source' => 'customer', 'license_url' => 'https://example.com/lizenz', 'source_url' => 'javascript:alert(1)', ]); creditConfirmationFor($image); $credit = ImageCredit::for($image->fresh(), $company); expect($credit->line())->toBe('Foto: Anna Beispiel / Beispiel AG · KI-bearbeitet') ->and($credit->responsibleLine)->toBe('Bild und Angaben: Beispiel AG, eingereicht am 22. September 2026') ->and($credit->aiSourceLabel)->toBe('Angabe des Einreichers') ->and($credit->licenseUrl)->toBe('https://example.com/lizenz') ->and($credit->sourceUrl)->toBeNull() ->and($credit->pdfLines())->toContain('Rechte bestätigt am 22.09.2026 10:00 (img-confirm-v2, AGB 0.6)'); $this->get('https://presseecho.test/de/press-release/bild-nachweis-test') ->assertSuccessful() ->assertSeeText('Foto: Anna Beispiel / Beispiel AG · KI-bearbeitet') ->assertSee('data-image-credit', false) ->assertSeeText('Dieses Bild wurde mit KI bearbeitet.') ->assertSeeText('Kennzeichnung: Angabe des Einreichers') ->assertSeeText('Bild und Angaben: Beispiel AG, eingereicht am 22. September 2026') ->assertSeeText('Bild melden') ->assertDontSee('javascript:alert', false); }); test('a legacy image without credit falls back to the company as source and shows no responsible line', function () { /** @var TestCase $this */ ['release' => $release, 'company' => $company] = creditPublishedRelease(); $image = PressReleaseImage::factory()->for($release)->legacy()->create(['is_ai_generated' => true]); $credit = ImageCredit::for($image, $company); expect($credit->line())->toBe('Quelle: Beispiel AG · KI-generiert') ->and($credit->responsibleLine)->toBeNull() ->and($credit->aiSourceLabel)->toBeNull() ->and($credit->isLegacy)->toBeTrue(); $this->get('https://presseecho.test/de/press-release/bild-nachweis-test') ->assertSuccessful() ->assertSeeText('Quelle: Beispiel AG · KI-generiert') ->assertDontSeeText('Bild und Angaben'); }); test('a legacy image with author but without copyright uses the author and the old fields stay in the pdf lines', function () { ['release' => $release, 'company' => $company] = creditPublishedRelease(); $image = PressReleaseImage::factory()->for($release)->legacy()->create([ 'author' => 'Max Mustermann', 'license_type' => 'own', ]); $credit = ImageCredit::for($image, $company); expect($credit->line())->toBe('Foto: Max Mustermann') ->and($credit->pdfLines())->toBe(['Bildnachweis: Max Mustermann']); }); test('an image without any detail renders no line and no overlay', function () { /** @var TestCase $this */ ['release' => $release] = creditPublishedRelease(); PressReleaseImage::factory()->for($release)->legacy()->create(); $release->company()->update(['name' => '']); $credit = ImageCredit::for($release->images()->first(), $release->company()->first()); expect($credit->line())->toBeNull() ->and($credit->hasDetails())->toBeFalse(); }); test('the guidelines page describes the automated check and the image rules under version 0.2', function () { /** @var TestCase $this */ $this->seed(LegalDocumentSeeder::class); expect(LegalDocument::current(LegalDocument::TYPE_RICHTLINIEN)?->version)->toBe('0.2'); $this->get('https://presseecho.test/de/richtlinien') ->assertSuccessful() ->assertSeeText('Jede Mitteilung durchläuft vor der Veröffentlichung eine automatisierte Prüfung.') ->assertSeeText('Zu jedem Bild gehört ein Bildnachweis') ->assertDontSeeText('geprüft, den Richtlinien entsprechend'); });