presseportale/tests/Feature/Web/CategoryArchiveTest.php
Kevin Adametz 4616bb7aad web: Jahresarchiv je Branche, Kategoriebeschreibungen, Portalname im Titel (Roadmap 2.3)
- /de/category/{slug}/archiv/{jahr} (CategoryArchivePageData,
  CategoryArchiveController, View kategorie-archiv): 30 Meldungen je
  Seite, neueste zuerst, Jahresleiste mit Zahlen; ohne Jahr das juengste
  Jahr mit Meldungen. Canonical mit ?page=N auf Folgeseiten, hreflang
  nur auf Seite 1, CollectionPage + BreadcrumbList. Gecacht bis Seite 3.
  Leere Jahre, unbekannte Kategorien, leere Folgeseiten: 404.
- Branchenseite: Jahresleiste unter dem Feed (archiveYears, eine
  gruppierte Query); Sitemap listet alle Jahresarchive.
- categories:seed-descriptions {--dry-run} {--force}: fuellt leere DE/EN
  Beschreibungen aus database/seeders/data/category-descriptions.php
  (14 Kategorien, sachlich, ohne Werbeaussagen), leert den Seiten-Cache.
- Branchenseite: Titel trug config('app.name') ("– pressekonto") statt
  des Portalnamens — jetzt presseecho/businessportal24.
- Protokoll Teil 5 (2.3 mit Befund: 0 von 14 Beschreibungen, Sonstiges =
  28 % des Bestands), Roadmap 2.3 abgehakt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 10:25:29 +00:00

111 lines
4.9 KiB
PHP

<?php
use App\Enums\Portal;
use App\Models\Category;
use App\Models\Company;
use App\Models\PressRelease;
use App\Services\Web\CategoryArchivePageData;
use Illuminate\Support\Str;
use Tests\TestCase;
/**
* Jahresarchiv einer Branche (Roadmap 2.3): /de/category/{slug}/archiv/{jahr}.
*/
function archiveCategory(): Category
{
$category = Category::factory()->create(['portal' => Portal::Presseecho, 'is_active' => true]);
$category->translations()->create(['locale' => 'de', 'name' => 'Handel', 'slug' => 'handel']);
$category->translations()->create(['locale' => 'en', 'name' => 'Commerce', 'slug' => 'commerce']);
return $category;
}
function archiveRelease(Category $category, string $slug, string $publishedAt, array $attributes = []): PressRelease
{
return PressRelease::factory()
->published()
->forPortal(Portal::Presseecho)
->for($category)
->for(Company::factory()->presseecho()->create(['name' => 'Archiv GmbH']))
->create(['slug' => $slug, 'published_at' => $publishedAt, ...$attributes]);
}
test('the archive lists the releases of one year, newest first, with a year navigation', function () {
/** @var TestCase $this */
$category = archiveCategory();
archiveRelease($category, 'alt-2019', '2019-03-01 10:00:00', ['title' => 'Meldung aus 2019']);
archiveRelease($category, 'alt-2019-spaeter', '2019-11-01 10:00:00', ['title' => 'Spätere Meldung 2019']);
archiveRelease($category, 'alt-2021', '2021-05-01 10:00:00', ['title' => 'Meldung aus 2021']);
$response = $this->get('https://presseecho.test/de/category/handel/archiv/2019')->assertSuccessful();
$response
->assertSee('Pressemitteilungen Handel 2019')
->assertSee('Spätere Meldung 2019')
->assertSee('Meldung aus 2019')
->assertSee('Archiv GmbH')
->assertSee('/de/category/handel/archiv/2021', false)
->assertSee('<link rel="canonical" href="https://presseecho.test/de/category/handel/archiv/2019">', false)
->assertSee('"@type":"CollectionPage"', false)
->assertSee('hreflang="en" href="https://presseecho.test/en/category/commerce/archiv/2019"', false);
// Nur die Liste prüfen — der Live-Ticker im Kopf zeigt die jüngste Meldung (2021).
$list = Str::between((string) $response->getContent(), '<ol class="m-0 p-0 list-none">', '</ol>');
expect($list)->toContain('Spätere Meldung 2019');
$this->assertStringNotContainsString('Meldung aus 2021', $list);
expect(strpos($list, 'Spätere Meldung 2019'))->toBeLessThan((int) strpos($list, 'Meldung aus 2019'));
});
test('without a year the archive opens the latest year with releases', function () {
/** @var TestCase $this */
$category = archiveCategory();
archiveRelease($category, 'alt-2019', '2019-03-01 10:00:00');
archiveRelease($category, 'alt-2021', '2021-05-01 10:00:00', ['title' => 'Meldung aus 2021']);
$this->get('https://presseecho.test/de/category/handel/archiv')
->assertSuccessful()
->assertSee('Pressemitteilungen Handel 2021')
->assertSee('Meldung aus 2021');
});
test('years without releases, unknown categories and empty pages are 404', function () {
/** @var TestCase $this */
$category = archiveCategory();
archiveRelease($category, 'alt-2019', '2019-03-01 10:00:00');
$this->get('https://presseecho.test/de/category/handel/archiv/2018')->assertNotFound();
$this->get('https://presseecho.test/de/category/gibt-es-nicht/archiv/2019')->assertNotFound();
$this->get('https://presseecho.test/de/category/handel/archiv/2019?page=2')->assertNotFound();
});
test('follow-up pages carry the page in canonical and title', function () {
/** @var TestCase $this */
$category = archiveCategory();
foreach (range(1, CategoryArchivePageData::PerPage + 1) as $i) {
archiveRelease($category, 'meldung-'.$i, sprintf('2020-01-%02d 10:00:00', min($i, 28)), ['title' => 'Meldung Nr. '.$i]);
}
$this->get('https://presseecho.test/de/category/handel/archiv/2020?page=2')
->assertSuccessful()
->assertSee('<link rel="canonical" href="https://presseecho.test/de/category/handel/archiv/2020?page=2">', false)
->assertSee('Seite 2')
->assertDontSee('hreflang=', false);
});
test('the category page links its archive years and the sitemap lists them', function () {
/** @var TestCase $this */
$category = archiveCategory();
archiveRelease($category, 'alt-2019', '2019-03-01 10:00:00');
archiveRelease($category, 'neu', now()->subDay()->toDateTimeString());
$this->get('https://presseecho.test/de/category/handel')
->assertSuccessful()
->assertSee('Archiv Handel')
->assertSee('/de/category/handel/archiv/2019', false)
->assertSee('/de/category/handel/archiv/'.now()->year, false);
$this->get('https://presseecho.test/sitemap.xml')
->assertSuccessful()
->assertSee('<loc>https://presseecho.test/de/category/handel/archiv/2019</loc>', false);
});