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>
This commit is contained in:
parent
b60a28dc9c
commit
4616bb7aad
15 changed files with 793 additions and 13 deletions
111
tests/Feature/Web/CategoryArchiveTest.php
Normal file
111
tests/Feature/Web/CategoryArchiveTest.php
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<?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);
|
||||
});
|
||||
44
tests/Feature/Web/SeedCategoryDescriptionsTest.php
Normal file
44
tests/Feature/Web/SeedCategoryDescriptionsTest.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Models\Category;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* `categories:seed-descriptions` (Roadmap 2.3): füllt nur leere Felder,
|
||||
* --force überschreibt, Dry-Run schreibt nichts.
|
||||
*/
|
||||
test('the command fills empty descriptions from the data file and keeps existing ones', function () {
|
||||
/** @var TestCase $this */
|
||||
$handel = Category::factory()->create(['portal' => Portal::Both, 'is_active' => true]);
|
||||
$handel->translations()->create(['locale' => 'de', 'name' => 'Handel', 'slug' => 'handel', 'description' => null]);
|
||||
$handel->translations()->create(['locale' => 'en', 'name' => 'Commerce', 'slug' => 'commerce', 'description' => 'Own text']);
|
||||
|
||||
$this->artisan('categories:seed-descriptions', ['--dry-run' => true])
|
||||
->expectsOutputToContain('[Dry-Run] Nichts geschrieben.')
|
||||
->assertSuccessful();
|
||||
|
||||
expect($handel->translations()->where('locale', 'de')->value('description'))->toBeNull();
|
||||
|
||||
$this->artisan('categories:seed-descriptions')->assertSuccessful();
|
||||
|
||||
expect($handel->translations()->where('locale', 'de')->value('description'))->toStartWith('Pressemitteilungen aus dem Handel')
|
||||
->and($handel->translations()->where('locale', 'en')->value('description'))->toBe('Own text');
|
||||
|
||||
$this->artisan('categories:seed-descriptions', ['--force' => true])->assertSuccessful();
|
||||
|
||||
expect($handel->translations()->where('locale', 'en')->value('description'))->toStartWith('Press releases from retail');
|
||||
});
|
||||
|
||||
test('every data file entry has a german and an english text within the admin limit', function () {
|
||||
/** @var array<string, array<string, string>> $texts */
|
||||
$texts = require database_path('seeders/data/category-descriptions.php');
|
||||
|
||||
expect($texts)->toHaveCount(14);
|
||||
|
||||
foreach ($texts as $slug => $byLocale) {
|
||||
expect($byLocale)->toHaveKeys(['de', 'en']);
|
||||
expect(mb_strlen($byLocale['de']))->toBeLessThanOrEqual(1000);
|
||||
expect(mb_strlen($byLocale['en']))->toBeLessThanOrEqual(1000);
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue