Backend-WIP: Kontakt-Queue, Page-Cache, Validierungs-Feedback, i18n
Gebuendelt, da ueber gemeinsame lang-/Service-Dateien verschraenkt: - Kontakt-Nachrichten: contact_messages-Tabelle + Model/Enums/Factory, Admin-Queue (livewire/admin/contact-messages), ContactController persistiert das Anliegen zusaetzlich zur Brand-Mail - Page-Data-Cache (Services/Web/*PageData) + web-archive-Indizes auf press_releases - Validierungs-Feedback + Company-Portal-Restriction (Feature-Tests) - i18n-Auslagerung lang/de|en (auth, passwords, validation, static) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c6b5ec8479
commit
fd8e050cb7
60 changed files with 3963 additions and 327 deletions
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Enums\PressReleasePlaceholder;
|
||||
use App\Enums\PressReleaseStatus;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
|
|
@ -80,15 +81,20 @@ test('the ad-hoc ticker also renders on subpages', function () {
|
|||
'published_at' => now()->subHour(),
|
||||
]);
|
||||
|
||||
// Ticker-Meldungen sind anklickbar und führen zur Detailseite. Der Host der
|
||||
// generierten URL stammt in Tests aus APP_URL (ThemeServiceProvider bootet
|
||||
// vor dem Test-Request), daher wird nur der Pfad geprüft.
|
||||
$this->get('https://businessportal24.test/de/newsrooms')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('AD-HOC')
|
||||
->assertSeeText('Ticker-Meldung auf Unterseite');
|
||||
->assertSeeText('Ticker-Meldung auf Unterseite')
|
||||
->assertSee('/de/press-release/ticker-meldung-auf-unterseite', false);
|
||||
|
||||
$this->get('https://businessportal24.test/de/preise')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('AD-HOC')
|
||||
->assertSeeText('Ticker-Meldung auf Unterseite');
|
||||
->assertSeeText('Ticker-Meldung auf Unterseite')
|
||||
->assertSee('/de/press-release/ticker-meldung-auf-unterseite', false);
|
||||
});
|
||||
|
||||
test('businessportal24 homepage hides data sections and shows the empty feed state without content', function () {
|
||||
|
|
@ -170,6 +176,38 @@ test('businessportal24 homepage feed only shows published businessportal content
|
|||
->assertDontSeeText('Entwurf nicht sichtbar');
|
||||
});
|
||||
|
||||
test('businessportal24 homepage renders the chosen placeholder for image-less releases', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = Category::factory()->create([
|
||||
'portal' => Portal::Businessportal24,
|
||||
]);
|
||||
$category->translations()->create([
|
||||
'locale' => 'de',
|
||||
'name' => 'Energie & Klima',
|
||||
'slug' => 'energie-klima',
|
||||
]);
|
||||
|
||||
$company = Company::factory()->businessportal24()->create([
|
||||
'name' => 'Muster Energie GmbH',
|
||||
]);
|
||||
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Meldung mit gewähltem Platzhalter',
|
||||
'slug' => 'meldung-mit-gewaehltem-platzhalter',
|
||||
'placeholder_variant' => PressReleasePlaceholder::DotsAmber,
|
||||
'published_at' => now()->subHour(),
|
||||
]);
|
||||
|
||||
$this->get('https://businessportal24.test/de')
|
||||
->assertSuccessful()
|
||||
->assertSee('images/press-release-placeholders/09-dots-amber.svg', false);
|
||||
});
|
||||
|
||||
test('businessportal24 homepage shows most read releases in the sidebar', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = Category::factory()->create([
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\ContactMessageTopic;
|
||||
use App\Mail\ContactRequestSubmitted;
|
||||
use App\Models\ContactMessage;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Tests\TestCase;
|
||||
|
||||
test('the hub landing renders the contact modal form', function () {
|
||||
/** @var TestCase $this */
|
||||
// Die frühere Kontakt-Seite wurde zurückgebaut; das Formular lebt im
|
||||
// Kontakt-Modal des Hubs und postet weiter auf kontakt.store.
|
||||
// schwebenden Kontakt-Widget und postet weiter auf kontakt.store.
|
||||
$this->get('https://pressekonto.test/')
|
||||
->assertSuccessful()
|
||||
->assertSee('name="name"', false)
|
||||
|
|
@ -16,6 +18,45 @@ test('the hub landing renders the contact modal form', function () {
|
|||
->assertSee('Nachricht senden');
|
||||
});
|
||||
|
||||
test('the spoke pages render the floating contact launcher', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/de/faq')
|
||||
->assertSuccessful()
|
||||
->assertSee('open-contact-modal', false)
|
||||
->assertSee(route('kontakt.store', ['edition' => 'de']), false)
|
||||
->assertSee('Fehler melden')
|
||||
->assertSee('Pressemitteilung melden');
|
||||
});
|
||||
|
||||
test('the english edition renders the launcher with english strings', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/en/suche')
|
||||
->assertSuccessful()
|
||||
->assertSee('Report a bug')
|
||||
->assertSee('Report a press release')
|
||||
->assertSee('Send message')
|
||||
->assertDontSee('Fehler melden');
|
||||
});
|
||||
|
||||
test('a submission on the english edition flashes an english confirmation', function () {
|
||||
/** @var TestCase $this */
|
||||
Mail::fake();
|
||||
|
||||
$this->post('https://businessportal24.test/en/kontakt', [
|
||||
'name' => 'Paula Press',
|
||||
'email' => 'paula@example.test',
|
||||
'message' => 'I have a question about your platform, please get back to me.',
|
||||
])->assertSessionHas('contact-status', __('web.launcher.success', [], 'en'));
|
||||
});
|
||||
|
||||
test('the login page renders the floating contact launcher', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://pressekonto.test/login')
|
||||
->assertSuccessful()
|
||||
->assertSee('open-contact-modal', false)
|
||||
->assertSee(route('kontakt.store', ['edition' => 'de']), false);
|
||||
});
|
||||
|
||||
test('a valid contact request sends a mail to the configured recipient', function () {
|
||||
/** @var TestCase $this */
|
||||
Mail::fake();
|
||||
|
|
@ -66,6 +107,64 @@ test('a contact request from the hub domain goes to the pressekonto brand addres
|
|||
Mail::assertSent(ContactRequestSubmitted::class, fn (ContactRequestSubmitted $mail) => $mail->hasTo('kontakt@pressekonto.de'));
|
||||
});
|
||||
|
||||
test('a contact request is stored in the contact queue', function () {
|
||||
/** @var TestCase $this */
|
||||
Mail::fake();
|
||||
|
||||
$this->post('https://businessportal24.test/de/kontakt', [
|
||||
'name' => 'Paula Presse',
|
||||
'email' => 'paula@example.test',
|
||||
'message' => 'Ich habe eine Frage zu Ihrer Plattform und bitte um Rückruf.',
|
||||
])->assertSessionHas('contact-status');
|
||||
|
||||
$message = ContactMessage::query()->sole();
|
||||
|
||||
expect($message->topic)->toBe(ContactMessageTopic::Contact)
|
||||
->and($message->portal)->toBe('businessportal24')
|
||||
->and($message->name)->toBe('Paula Presse')
|
||||
->and($message->email)->toBe('paula@example.test')
|
||||
->and($message->status->isOpen())->toBeTrue();
|
||||
});
|
||||
|
||||
test('a widget submission stores topic and page url', function () {
|
||||
/** @var TestCase $this */
|
||||
Mail::fake();
|
||||
|
||||
$this->post('https://presseecho.test/de/kontakt', [
|
||||
'name' => 'Bernd Bug',
|
||||
'email' => 'bernd@example.test',
|
||||
'message' => 'Die Suche liefert auf der Startseite einen Fehler 500.',
|
||||
'topic' => 'bug_report',
|
||||
'page_url' => 'https://presseecho.test/de/suche',
|
||||
])->assertSessionHas('contact-status');
|
||||
|
||||
$message = ContactMessage::query()->sole();
|
||||
|
||||
expect($message->topic)->toBe(ContactMessageTopic::BugReport)
|
||||
->and($message->portal)->toBe('presseecho')
|
||||
->and($message->page_url)->toBe('https://presseecho.test/de/suche');
|
||||
|
||||
Mail::assertSent(ContactRequestSubmitted::class, function (ContactRequestSubmitted $mail) {
|
||||
return $mail->topicLabel === 'Fehlermeldung'
|
||||
&& $mail->pageUrl === 'https://presseecho.test/de/suche';
|
||||
});
|
||||
});
|
||||
|
||||
test('an unknown topic is rejected', function () {
|
||||
/** @var TestCase $this */
|
||||
Mail::fake();
|
||||
|
||||
$this->post('https://businessportal24.test/de/kontakt', [
|
||||
'name' => 'Paula Presse',
|
||||
'email' => 'paula@example.test',
|
||||
'message' => 'Ich habe eine Frage zu Ihrer Plattform und bitte um Rückruf.',
|
||||
'topic' => 'not-a-topic',
|
||||
])->assertSessionHasErrors(['topic']);
|
||||
|
||||
expect(ContactMessage::query()->count())->toBe(0);
|
||||
Mail::assertNothingSent();
|
||||
});
|
||||
|
||||
test('honeypot submissions are silently accepted without sending mail', function () {
|
||||
/** @var TestCase $this */
|
||||
Mail::fake();
|
||||
|
|
@ -78,6 +177,7 @@ test('honeypot submissions are silently accepted without sending mail', function
|
|||
])->assertSessionHas('contact-status');
|
||||
|
||||
Mail::assertNothingSent();
|
||||
expect(ContactMessage::query()->count())->toBe(0);
|
||||
});
|
||||
|
||||
test('the contact form validates its inputs', function () {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Models\Category;
|
||||
use Tests\TestCase;
|
||||
|
||||
test('header and footer link to faq, hilfe and legal pages and drop dead links', function () {
|
||||
|
|
@ -15,8 +17,10 @@ test('header and footer link to faq, hilfe and legal pages and drop dead links',
|
|||
->assertSee('/de/richtlinien', false)
|
||||
->assertSee('/de/melden', false)
|
||||
->assertDontSee('/de/cookies', false)
|
||||
// „Anmelden" führt jetzt in den Hub statt auf einen toten Anker.
|
||||
// „Anmelden" öffnet ein Modal, das Pressekonto erklärt und in den Hub verlinkt.
|
||||
->assertSee('/login', false)
|
||||
->assertSeeText(__('web.header.login_modal_title'))
|
||||
->assertSeeText(__('web.header.login_modal_hub'))
|
||||
->assertDontSee('href="#newsletter"', false)
|
||||
->assertDontSee('href="#rss"', false)
|
||||
->assertDontSee('href="#anmelden"', false)
|
||||
|
|
@ -24,3 +28,26 @@ test('header and footer link to faq, hilfe and legal pages and drop dead links',
|
|||
->assertDontSee('Mediendaten')
|
||||
->assertDontSee('Verifizierter Newsroom');
|
||||
});
|
||||
|
||||
test('categories beyond the top nine appear in the header more dropdown', function () {
|
||||
/** @var TestCase $this */
|
||||
// 12 Top-Level-Rubriken: die Top 9 erscheinen als Tabs, der Rest im
|
||||
// „Mehr“-Dropdown (Desktop) bzw. in der Mobile-Liste — keine Rubrik
|
||||
// fällt mehr komplett aus der Navigation.
|
||||
$categories = Category::factory()
|
||||
->count(12)
|
||||
->state(['portal' => Portal::Both->value])
|
||||
->withTranslations()
|
||||
->create();
|
||||
|
||||
$response = $this->get('https://businessportal24.test/de');
|
||||
|
||||
$response->assertSuccessful()
|
||||
->assertSeeText(__('web.nav.more'));
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$translation = $category->translations()->where('locale', 'de')->first();
|
||||
|
||||
$response->assertSeeText($translation->name);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
160
tests/Feature/Web/PageDataCacheTest.php
Normal file
160
tests/Feature/Web/PageDataCacheTest.php
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\PressRelease;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Livewire\Volt\Volt as LivewireVolt;
|
||||
use Tests\TestCase;
|
||||
|
||||
function makeCategoryWithSlug(Portal $portal, string $name, string $slug, ?int $parentId = null): Category
|
||||
{
|
||||
$category = Category::factory()->create(['portal' => $portal, 'parent_id' => $parentId]);
|
||||
$category->translations()->create([
|
||||
'locale' => 'de',
|
||||
'name' => $name,
|
||||
'slug' => $slug,
|
||||
]);
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
test('homepage data is cached per portal and language', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = makeCategoryWithSlug(Portal::Presseecho, 'Energie & Klima', 'energie-klima');
|
||||
$company = Company::factory()->presseecho()->create();
|
||||
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Presseecho)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Erste Meldung im Cache',
|
||||
'slug' => 'erste-meldung-im-cache',
|
||||
'published_at' => now()->subHour(),
|
||||
]);
|
||||
|
||||
$this->get('https://presseecho.test/de')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Erste Meldung im Cache');
|
||||
|
||||
expect(Cache::has('web.home.presseecho.de'))->toBeTrue();
|
||||
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Presseecho)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Neue Meldung nach Cache-Aufbau',
|
||||
'slug' => 'neue-meldung-nach-cache-aufbau',
|
||||
'published_at' => now()->subMinutes(5),
|
||||
]);
|
||||
|
||||
// Innerhalb der TTL liefert HomePageData den gecachten Stand: Lead bleibt
|
||||
// die alte Meldung. (Der Livewire-Feed ist bewusst ungecacht und zeigt
|
||||
// neue Meldungen sofort.)
|
||||
$cachedLead = $this->get('https://presseecho.test/de')
|
||||
->assertSuccessful()
|
||||
->viewData('leadRelease');
|
||||
|
||||
expect($cachedLead->title)->toBe('Erste Meldung im Cache');
|
||||
|
||||
Cache::flush();
|
||||
|
||||
$freshLead = $this->get('https://presseecho.test/de')
|
||||
->assertSuccessful()
|
||||
->viewData('leadRelease');
|
||||
|
||||
expect($freshLead->title)->toBe('Neue Meldung nach Cache-Aufbau');
|
||||
});
|
||||
|
||||
test('category page data is cached per portal, language and slug but 404s are not cached', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = makeCategoryWithSlug(Portal::Businessportal24, 'Mobilität', 'mobilitaet');
|
||||
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($category)
|
||||
->for(Company::factory()->businessportal24())
|
||||
->create([
|
||||
'slug' => 'mobilitaets-meldung',
|
||||
'published_at' => now()->subDay(),
|
||||
]);
|
||||
|
||||
$this->get('https://businessportal24.test/de/category/mobilitaet')->assertSuccessful();
|
||||
|
||||
expect(Cache::has('web.category.businessportal24.de.mobilitaet'))->toBeTrue();
|
||||
|
||||
$this->get('https://businessportal24.test/de/category/gibt-es-nicht')->assertNotFound();
|
||||
|
||||
expect(Cache::has('web.category.businessportal24.de.gibt-es-nicht'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('subcategory counts are aggregated correctly in a single grouped query', function () {
|
||||
/** @var TestCase $this */
|
||||
$parent = makeCategoryWithSlug(Portal::Both, 'Wirtschaft', 'wirtschaft');
|
||||
$child = makeCategoryWithSlug(Portal::Both, 'Handel', 'handel', $parent->id);
|
||||
|
||||
$company = Company::factory()->businessportal24()->create();
|
||||
|
||||
// Stichtag (referenceNow) = jüngste Meldung: 2 im 7-Tage-Fenster, 1 älter (>14 Tage).
|
||||
foreach ([1, 2, 20] as $daysAgo) {
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($child)
|
||||
->for($company)
|
||||
->create([
|
||||
'slug' => "handel-meldung-{$daysAgo}",
|
||||
'published_at' => now()->subDays($daysAgo),
|
||||
]);
|
||||
}
|
||||
|
||||
$response = $this->get('https://businessportal24.test/de/category/wirtschaft')->assertSuccessful();
|
||||
|
||||
$subCategories = $response->viewData('subCategories');
|
||||
|
||||
expect($subCategories)->toHaveCount(1)
|
||||
->and($subCategories[0]['name'])->toBe('Handel')
|
||||
->and($subCategories[0]['total'])->toBe(3)
|
||||
->and($subCategories[0]['recent'])->toBe(2)
|
||||
->and($subCategories[0]['delta'])->toBe(2);
|
||||
});
|
||||
|
||||
test('the press release feed today filter matches the reference day via range comparison', function () {
|
||||
/** @var TestCase $this */
|
||||
$category = makeCategoryWithSlug(Portal::Presseecho, 'Technologie', 'technologie');
|
||||
$company = Company::factory()->presseecho()->create();
|
||||
|
||||
// Historisches Archiv: Stichtag = jüngste Meldung (vor 10 Tagen).
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Presseecho)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Meldung am Stichtag',
|
||||
'slug' => 'meldung-am-stichtag',
|
||||
'published_at' => now()->subDays(10)->setTime(9, 0),
|
||||
]);
|
||||
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Presseecho)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Meldung vom Vortag',
|
||||
'slug' => 'meldung-vom-vortag',
|
||||
'published_at' => now()->subDays(11)->setTime(9, 0),
|
||||
]);
|
||||
|
||||
LivewireVolt::test('web.press-release-feed', ['portal' => Portal::Presseecho->value, 'language' => 'de'])
|
||||
->call('setTimeframe', 'today')
|
||||
->assertSee('Meldung am Stichtag')
|
||||
->assertDontSee('Meldung vom Vortag');
|
||||
});
|
||||
|
|
@ -101,6 +101,25 @@ test('the hub page speaks of Pruefung without the redaktionell qualifier', funct
|
|||
->assertSeeText('Prüfung inklusive');
|
||||
});
|
||||
|
||||
test('the hub links faq and help center in header and footer', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->seed(PlanSeeder::class);
|
||||
|
||||
$response = $this->get('https://pressekonto.test/')->assertSuccessful();
|
||||
$content = $response->getContent();
|
||||
|
||||
// FAQ + Hilfe sind in der Top-Utility-Bar (neben Kontakt) und im
|
||||
// Footer unter „Plattform" verlinkt.
|
||||
expect($content)
|
||||
->toContain('/de/faq')
|
||||
->toContain('/de/hilfe')
|
||||
->toContain('Hilfe-Center');
|
||||
|
||||
// Beide Ziele sind auf der Hub-Domain erreichbar.
|
||||
$this->get('https://pressekonto.test/de/faq')->assertSuccessful();
|
||||
$this->get('https://pressekonto.test/de/hilfe')->assertSuccessful();
|
||||
});
|
||||
|
||||
test('legal pages are reachable on the hub domain', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://pressekonto.test/de/impressum')->assertSuccessful();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use App\Enums\CompanyType;
|
||||
use App\Enums\Portal;
|
||||
use App\Enums\PressReleasePlaceholder;
|
||||
use App\Enums\PressReleaseStatus;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
|
|
@ -71,7 +72,58 @@ test('businessportal24 release detail renders the editorial shell with real data
|
|||
->assertSeeText('Annika Werthmann')
|
||||
->assertSeeText('Dekarbonisierung der Schwerindustrie')
|
||||
->assertSeeText('Über das Unternehmen')
|
||||
->assertSeeText('Wasserstoff');
|
||||
->assertSeeText('Wasserstoff')
|
||||
// Magic-Link-Hinweis: hinterlegte Kontakte verwalten die PM im pressekonto.
|
||||
->assertSeeText('Sie sind als Firma oder Pressekontakt dieser Mitteilung hinterlegt?')
|
||||
->assertSee(rtrim(config('domains.domain_portal_url'), '/').'/anmeldelink', false);
|
||||
});
|
||||
|
||||
test('release detail without image embeds the headline in the generated hero', function () {
|
||||
/** @var TestCase $this */
|
||||
[$category, $company] = detailCategoryAndCompany(Portal::Businessportal24);
|
||||
|
||||
$release = PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Meldung ohne Pressefoto',
|
||||
'slug' => 'meldung-ohne-pressefoto',
|
||||
'placeholder_variant' => PressReleasePlaceholder::LinesGreen,
|
||||
]);
|
||||
|
||||
$this->get('https://businessportal24.test/de/press-release/'.$release->slug)
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Meldung ohne Pressefoto')
|
||||
->assertSee('bg-hero-grad', false)
|
||||
// Es muss dieselbe Platzhalter-Variante wie im User-Panel erscheinen.
|
||||
->assertSee('images/press-release-placeholders/05-lines-green.svg', false)
|
||||
->assertDontSee('Bild · Pressefoto');
|
||||
});
|
||||
|
||||
test('release detail without chosen placeholder falls back to the generic hero', function () {
|
||||
/** @var TestCase $this */
|
||||
// Altbestand: PMs ohne gesetzte `placeholder_variant` rendern weiterhin
|
||||
// die generische Hero-Fallback-Grafik statt einer Platzhalter-SVG.
|
||||
[$category, $company] = detailCategoryAndCompany(Portal::Businessportal24);
|
||||
|
||||
$release = PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create([
|
||||
'title' => 'Altbestand ohne Platzhalter-Variante',
|
||||
'slug' => 'altbestand-ohne-variante',
|
||||
]);
|
||||
|
||||
PressRelease::withoutGlobalScopes()->whereKey($release->id)->update(['placeholder_variant' => null]);
|
||||
|
||||
$this->get('https://businessportal24.test/de/press-release/'.$release->slug)
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Altbestand ohne Platzhalter-Variante')
|
||||
->assertDontSee('images/press-release-placeholders/', false);
|
||||
});
|
||||
|
||||
test('release detail shows prominent report links below the article', function () {
|
||||
|
|
@ -257,5 +309,39 @@ test('release detail shows more from newsroom and related releases', function ()
|
|||
->assertSeeText('Mehr von Muster Energie GmbH')
|
||||
->assertSeeText('Weitere Meldung desselben Newsrooms')
|
||||
->assertSeeText('Verwandte Meldungen')
|
||||
->assertSeeText('Verwandte Branchenmeldung');
|
||||
->assertSeeText('Verwandte Branchenmeldung')
|
||||
->assertSee('grid grid-cols-1 gap-10 md:grid-cols-2', false);
|
||||
});
|
||||
|
||||
test('a single recommendation list spans the full layout width', function () {
|
||||
/** @var TestCase $this */
|
||||
[$category, $company] = detailCategoryAndCompany(Portal::Businessportal24);
|
||||
|
||||
$main = PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($category)
|
||||
->for($company)
|
||||
->create(['slug' => 'einzelliste-hauptmeldung', 'published_at' => now()->subDay()]);
|
||||
|
||||
$otherCompany = Company::factory()->create([
|
||||
'portal' => Portal::Businessportal24,
|
||||
'name' => 'Andere Industrie AG',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
// Nur verwandte Meldungen (andere Firma, gleiche Rubrik) — kein
|
||||
// "Mehr von …": die einzelne Liste darf nicht auf halber Breite stehen.
|
||||
PressRelease::factory()
|
||||
->published()
|
||||
->forPortal(Portal::Businessportal24)
|
||||
->for($category)
|
||||
->for($otherCompany)
|
||||
->create(['slug' => 'einzelliste-verwandt', 'published_at' => now()->subDays(2)]);
|
||||
|
||||
$this->get('https://businessportal24.test/de/press-release/'.$main->slug)
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Verwandte Meldungen')
|
||||
->assertSee('grid grid-cols-1 gap-10"', false)
|
||||
->assertDontSee('grid grid-cols-1 gap-10 md:grid-cols-2', false);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -75,6 +75,18 @@ test('the terms render the b2b version with cancellation and archive rules', fun
|
|||
->assertDontSeeText('Business Portal GmbH');
|
||||
});
|
||||
|
||||
test('the terms state that subscriptions renew for the chosen term instead of switching to monthly billing', function () {
|
||||
/** @var TestCase $this */
|
||||
// Jahresabos verlängern sich um jeweils zwölf Monate und sind zum
|
||||
// Laufzeitende kündbar — kein Wechsel auf monatliche Abrechnung mehr.
|
||||
$this->get('https://pressekonto.test/de/agb')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('verlängert sich jeweils automatisch um die')
|
||||
->assertSeeText('zum Ende des jeweiligen Zwölfmonats-Zeitraums')
|
||||
->assertDontSeeText('auf unbestimmte Zeit')
|
||||
->assertDontSeeText('erfolgt die Abrechnung nach Ablauf der Erstlaufzeit monatlich');
|
||||
});
|
||||
|
||||
test('the guidelines list the rejection categories with anchors', function () {
|
||||
/** @var TestCase $this */
|
||||
// Kategorien kommen live aus rejection_categories (E3) — neue Zeilen
|
||||
|
|
@ -108,7 +120,11 @@ test('the reporting page separates free legal paths from the paid takedown servi
|
|||
->assertSeeText('Ausübung gesetzlicher Rechte')
|
||||
// Depublikations-Preis (O6, 06.07.: 35 € netto) im Doppelausweis.
|
||||
->assertSeeText('35,00 € zzgl. USt.')
|
||||
->assertSeeText('41,65 € inkl. 19');
|
||||
->assertSeeText('41,65 € inkl. 19')
|
||||
// Keine Melde-E-Mail-Adresse mehr auf der Seite: alle Wege verweisen
|
||||
// auf das Kontakt-Widget (Badge unten rechts, wie im Footer).
|
||||
->assertDontSee('mailto:', false)
|
||||
->assertSeeText('Kontaktformular');
|
||||
});
|
||||
|
||||
test('the reporting page falls back to the neutral price wording when depublication is disabled', function () {
|
||||
|
|
@ -152,3 +168,76 @@ test('the help center shows quick links and topics', function () {
|
|||
->assertSee(route('faq'), false)
|
||||
->assertSee(route('veroeffentlichen'), false);
|
||||
});
|
||||
|
||||
dataset('static pages in english', [
|
||||
'faq' => ['en/faq', 'Frequently asked questions'],
|
||||
'hilfe' => ['en/hilfe', 'Help center'],
|
||||
'api' => ['en/api', 'API documentation'],
|
||||
'impressum' => ['en/impressum', 'Imprint'],
|
||||
'melden' => ['en/melden', 'Report content or request changes'],
|
||||
'kuendigung' => ['en/kuendigung', 'Cancel contracts here'],
|
||||
]);
|
||||
|
||||
test('the translated static pages render in english on the en edition', function (string $path, string $heading) {
|
||||
/** @var TestCase $this */
|
||||
$this->get("https://businessportal24.test/{$path}")
|
||||
->assertSuccessful()
|
||||
->assertSeeText($heading)
|
||||
// Kein deutsches Seiten-Gerüst mehr auf der EN-Ausgabe.
|
||||
->assertDontSeeText('Häufig gestellte Fragen')
|
||||
->assertDontSeeText('Hilfe-Center');
|
||||
})->with('static pages in english');
|
||||
|
||||
test('the publish page renders in english with an en hreflang alternate', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/en/veroeffentlichen')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Publish a press release.')
|
||||
->assertSeeText('Four principles that carry this platform')
|
||||
->assertSee('hreflang="en"', false)
|
||||
->assertDontSeeText('Pressemitteilung veröffentlichen.');
|
||||
});
|
||||
|
||||
test('the report forms render in english on the en edition', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://businessportal24.test/en/melden/rechtswidrig')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Report illegal content')
|
||||
->assertSeeText('Submit report');
|
||||
|
||||
$this->get('https://businessportal24.test/en/melden/kontaktdaten')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('Have your own contact details removed')
|
||||
->assertSeeText('Submit request');
|
||||
});
|
||||
|
||||
test('the binding legal texts stay german but show an english language notice on the en edition', function () {
|
||||
/** @var TestCase $this */
|
||||
// AGB/Datenschutz/Richtlinien bleiben deutsch (O-C: EN-Fassungen erst
|
||||
// nach fachanwaltlicher Prüfung) — die EN-Ausgabe erklärt das sichtbar.
|
||||
$this->get('https://businessportal24.test/en/agb')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('The German version is authoritative and legally binding.')
|
||||
->assertSeeText('Allgemeine Geschäftsbedingungen');
|
||||
|
||||
$this->get('https://businessportal24.test/en/datenschutz')
|
||||
->assertSuccessful()
|
||||
->assertSeeText('The German version is authoritative and legally binding.');
|
||||
|
||||
// Auf der deutschen Ausgabe erscheint kein Hinweis.
|
||||
$this->get('https://businessportal24.test/de/agb')
|
||||
->assertSuccessful()
|
||||
->assertDontSeeText('The German version is authoritative and legally binding.');
|
||||
});
|
||||
|
||||
test('german validation messages are used on the de edition forms', function () {
|
||||
/** @var TestCase $this */
|
||||
$response = $this->from('https://businessportal24.test/de/melden/rechtswidrig')
|
||||
->post('https://businessportal24.test/de/melden/rechtswidrig', []);
|
||||
|
||||
$response->assertSessionHasErrors(['reported_url', 'requester_name', 'requester_email', 'message']);
|
||||
|
||||
$messages = collect(session('errors')->all())->implode(' ');
|
||||
expect($messages)->toContain('muss ausgefüllt werden')
|
||||
->not->toContain('field is required');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue