E9: Kategorie-Zuordnung wie vorgeschlagen übernommen, Zuordnung ist standardmäßig an (CANONICAL_ASSIGNMENT_ENABLED). Auf der Dev-Kopie verifiziert: Kopie trägt rel=canonical auf die Erstquelle und fällt aus der Sitemap ihres Portals. E10: faq, hilfe, gute-pressemitteilung, die vier Transparenzseiten und die API-Doku lagen auf allen drei Hosts mit identischem Text und je eigenem Self-Canonical. Jetzt führt presseecho die Informationsseiten, der Hub die API-Doku; Sitemap und llms.txt nennen nur die führende Adresse. Rechtstexte (Impressum, Datenschutz, AGB, Richtlinien, Meldeweg, Kündigung) bleiben bewusst je Host — jede Domain ist ein eigenes Angebot mit eigener Anbieterkennzeichnung. Bei der Erreichbarkeitsprüfung des Hubs gefunden und behoben: /de/preise, /de/veroeffentlichen, /de/newsrooms und /de/suche wurden unter pressekonto.com mit dem Branding von businessportal24 ausgeliefert (der Portal-Kontext fällt dort darauf zurück) — jetzt 301 auf presseecho, die Transparenz-Navigation verlinkt direkt dorthin. /de/kontakt und /de/ueber-uns sprangen über /de auf die Startseite, jetzt in einem Schritt. Der Self-Canonical kommt jetzt aus dem aufgerufenen Host; Portal-Inhalte, die über den Hub laufen, behalten ihr Portal (webPortalContextApplied). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
131 lines
6.9 KiB
PHP
131 lines
6.9 KiB
PHP
<?php
|
||
|
||
use App\Models\Plan;
|
||
use App\Services\Web\PricingTransparencyData;
|
||
use Tests\TestCase;
|
||
|
||
/**
|
||
* Transparenzseiten (Roadmap 2.4): Zahlen aus der Konfiguration, kein
|
||
* Anbietername, Rechenbeispiel mit gemischtem Ausgang.
|
||
*/
|
||
beforeEach(function (): void {
|
||
config()->set('billing.single_pm_price_cents', 1900);
|
||
config()->set('billing.change_price_cents', 1000);
|
||
config()->set('billing.depublication_price_cents', 3500);
|
||
config()->set('billing.vat_rate', 0.19);
|
||
config()->set('credits.extra_pm.starter', 15);
|
||
|
||
Plan::factory()->create(['slug' => 'starter', 'name' => 'Starter', 'monthly_price_cents' => 2900, 'yearly_price_cents' => 29000, 'press_release_quota' => 3, 'is_active' => true]);
|
||
});
|
||
|
||
test('the worked example lets pay-per-release win in some rows and the plan in others', function () {
|
||
$data = app(PricingTransparencyData::class)->build();
|
||
|
||
expect($data['singleNet'])->toBe('19')
|
||
->and($data['singleGross'])->toBe('22,61')
|
||
->and($data['starterMonthlyNet'])->toBe('29')
|
||
->and(collect($data['examples'])->pluck('cheaper')->unique()->sort()->values()->all())->toBe(['single', 'starter']);
|
||
});
|
||
|
||
test('the cost page shows the short answer with the configured price and no competitor names', function () {
|
||
/** @var TestCase $this */
|
||
$response = $this->get('https://presseecho.test/de/was-kostet-eine-pressemitteilung')->assertSuccessful();
|
||
|
||
$response
|
||
->assertSee('Was kostet eine Pressemitteilung?')
|
||
->assertSee('<strong>19 € netto</strong> (22,61 € inkl. 19 % USt.)', false)
|
||
->assertSee('Woran man Folgekosten erkennt')
|
||
->assertSee('Rechenbeispiel')
|
||
->assertSee('228 €', false)
|
||
->assertSee('10 € netto')
|
||
->assertSee('35 € netto')
|
||
->assertSee('/de/pressemitteilung-loeschen-oder-aendern-lassen', false)
|
||
->assertSee('<title>Was kostet eine Pressemitteilung? Preis, Modelle, Rechenbeispiel – presseecho</title>', false);
|
||
|
||
// Kein Anbietername, keine fremden Preise (Briefing 17.09.2026).
|
||
$this->assertStringNotContainsStringIgnoringCase('presseportal.de', (string) $response->getContent());
|
||
$this->assertStringNotContainsStringIgnoringCase('openpr', (string) $response->getContent());
|
||
});
|
||
|
||
test('the no-subscription page and the change page render on every host in both editions', function (string $url) {
|
||
/** @var TestCase $this */
|
||
$this->get($url)->assertSuccessful();
|
||
})->with([
|
||
'https://businessportal24.test/de/pressemitteilung-veroeffentlichen-ohne-abo',
|
||
'https://businessportal24.test/en/pressemitteilung-veroeffentlichen-ohne-abo',
|
||
'https://pressekonto.test/de/pressemitteilung-loeschen-oder-aendern-lassen',
|
||
'https://presseecho.test/en/pressemitteilung-loeschen-oder-aendern-lassen',
|
||
'https://pressekonto.test/en/was-kostet-eine-pressemitteilung',
|
||
'https://presseecho.test/en/pressemitteilung-kostenlos-veroeffentlichen',
|
||
]);
|
||
|
||
test('the free page answers honestly, names our own link policy and shows the portal list only when configured', function () {
|
||
/** @var TestCase $this */
|
||
config()->set('transparency.free_portals', []);
|
||
|
||
$response = $this->get('https://businessportal24.test/de/pressemitteilung-kostenlos-veroeffentlichen')->assertSuccessful();
|
||
|
||
$response
|
||
->assertSee('Pressemitteilung kostenlos veröffentlichen')
|
||
->assertSee('Einen kostenlosen Weg gibt es nicht')
|
||
->assertSee('<code>rel="sponsored nofollow"</code>', false)
|
||
->assertSee('Warum kostenfreie Annahme und Prüfung sich ausschließen')
|
||
->assertSee('Wer bei kostenlosen Angeboten am Ende zahlt')
|
||
->assertDontSee('Kostenlose Presseportale</h2>', false)
|
||
->assertSee('aria-current="page">Kostenlos veröffentlichen?</span>', false)
|
||
->assertSee('/de/pressemitteilung-veroeffentlichen-ohne-abo', false);
|
||
|
||
config()->set('transparency.free_portals', [['name' => 'Beispielportal', 'url' => 'https://portal.example', 'note' => 'nur Text, keine Links']]);
|
||
config()->set('transparency.free_portals_checked_at', '2026-09-17');
|
||
|
||
$this->get('https://businessportal24.test/de/pressemitteilung-kostenlos-veroeffentlichen')->assertSuccessful()
|
||
->assertSee('Kostenlose Presseportale</h2>', false)
|
||
->assertSee('am 17.09.2026 kostenlos Einreichungen angenommen haben')
|
||
->assertSee('<a href="https://portal.example" rel="nofollow noopener" target="_blank">Beispielportal</a>', false)
|
||
->assertSee('nur Text, keine Links')
|
||
->assertSee('Kein Eintrag ist eine Empfehlung.');
|
||
});
|
||
|
||
test('the change page names the five ways with their prices and points to the self-service entry', function () {
|
||
/** @var TestCase $this */
|
||
$this->get('https://businessportal24.test/de/pressemitteilung-loeschen-oder-aendern-lassen')
|
||
->assertSuccessful()
|
||
->assertSee('Die fünf Wege')
|
||
->assertSee('kostenlos')
|
||
->assertSee('10 € netto')
|
||
->assertSee('35 € netto')
|
||
->assertSee('/de/melden/pm', false)
|
||
->assertSee('Die Meldung steht auf einem anderen Portal?')
|
||
// Unter-Navigation: aktuelle Seite markiert, Preisseite verlinkt.
|
||
->assertSee('aria-current="page">Meldung löschen oder ändern</span>', false)
|
||
->assertSee('/de/preise" class=', false);
|
||
});
|
||
|
||
test('the pages are linked from the pricing page, the help centre and the sitemap', function () {
|
||
/** @var TestCase $this */
|
||
$this->get('https://presseecho.test/de/preise')->assertSuccessful()
|
||
->assertSee('aria-label="Transparenz"', false)
|
||
->assertSee('aria-current="page">Tarife & Preise</span>', false)
|
||
->assertSee('/de/was-kostet-eine-pressemitteilung', false)
|
||
->assertSee('/de/pressemitteilung-veroeffentlichen-ohne-abo', false)
|
||
->assertSee('/de/pressemitteilung-loeschen-oder-aendern-lassen', false)
|
||
->assertSee('/de/pressemitteilung-kostenlos-veroeffentlichen', false);
|
||
|
||
$this->get('https://presseecho.test/de/hilfe')->assertSuccessful()
|
||
->assertSee('/de/pressemitteilung-loeschen-oder-aendern-lassen', false)
|
||
->assertSee('/de/pressemitteilung-veroeffentlichen-ohne-abo', false)
|
||
->assertSee('/de/pressemitteilung-kostenlos-veroeffentlichen', false);
|
||
|
||
$this->get('https://presseecho.test/sitemap.xml')->assertSuccessful()
|
||
->assertSee('<loc>https://presseecho.test/de/was-kostet-eine-pressemitteilung</loc>', false)
|
||
->assertSee('<loc>https://presseecho.test/de/pressemitteilung-kostenlos-veroeffentlichen</loc>', false);
|
||
|
||
$this->get('https://presseecho.test/llms.txt')->assertSuccessful()
|
||
->assertSee('/de/pressemitteilung-kostenlos-veroeffentlichen', false);
|
||
|
||
// Die Seiten liegen auf allen drei Hosts; in der Sitemap steht nur die
|
||
// führende Adresse (Anhang 2, E10) — beim Hub bleibt die API-Doku.
|
||
$this->get('https://pressekonto.test/sitemap.xml')->assertSuccessful()
|
||
->assertDontSee('/de/pressemitteilung-loeschen-oder-aendern-lassen', false)
|
||
->assertSee('<loc>https://pressekonto.test/de/api</loc>', false);
|
||
});
|