b2in/tests/Feature/ContentCorrections20261008Test.php
Kevin Adametz fcb10be120 Interior-Feinschliff: Hero-Slideshow, Lektorat, Rechtstexte, Aufräumen und Korrekturen 08.10.
Startseite und Heroes
- Hero-Slideshow (11 Motive inkl. Febrü) mit Infokarte, Laufzeit-Linie, größeren Klickflächen,
  Goldrahmen und mobiler Karte unter dem Bild; Zeile über der Überschrift, neue Vertrauenspunkte
- Hero-Stil auf Marken, Über uns und Kontakt übertragen, Breadcrumb entfernt
- Gründer-Leiste, Vision-Box auf Ocker, Showroom-Display in Box mit „Größer ansehen“/Vollbild
- Akzentfarbe #a8741f, größere Serif-Überschriften in der Interior-Welt
- Marken: Bildpause mit Zitat; Vereine aktualisiert und verlinkt

Inhalte
- Lektorat aller angezeigten Texte, /about-Korrekturen, Rechtstexte (DDG, TDDDG, Hosting Hetzner)
- Ablauf Maßanfertigung überall einheitlich, Magazin-Beitrag neu, alte Beiträge offline,
  deutsches Datum/Lesezeit, FAQ Projektausstattung, Otterpohl
- Referenzen-Seite per CMS-Schalter ausblendbar

CMS und Technik
- Tote Sektionen, Felder, Seiten und Komponenten entfernt und archiviert (dev/2026-08-28/archiv-*)
- Entwurf /entwurf, Farb-Schalter und /welcome entfernt; Test-Routen nur noch lokal
- Seitenbaum-Beschreibungen geprüft, fehlende CMS-Felder ergänzt, Markenliste aus „Kernmarken“
- Admin-Bildvorschauen repariert, verwendete Bilder in die Mediathek übernommen
- Seeder für das Deployment siehe dev/2026-08-28/entwicklungsplan.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:00:15 +00:00

148 lines
7.6 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\CmsArticle;
use App\Models\CmsBrand;
use Database\Seeders\CmsBrandSeeder;
use Database\Seeders\ContentCorrections20261008Seeder as Corrections;
use FluxCms\Core\Models\CmsContent;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
/**
* Korrekturen vom 08.10.2026 (dev/2026-08-28/Korrekturen-2026-10-08.md).
*/
function storedSection(string $group, string $key, array $de, array $en = []): CmsContent
{
return CmsContent::create(['group' => $group, 'key' => $key, 'type' => 'json', 'order' => 0,
'value' => array_filter(['de' => $de, 'en' => $en])]);
}
it('aligns the custom-made process in the stored texts and leaves own wording alone', function () {
$faq = storedSection('faq', 'faq', ['questions' => [
['question' => 'Beratung?', 'answer' => array_search(Corrections::FAQ_CONSULTATION_DE, Corrections::EXACT, true)],
['question' => 'Projekt?', 'answer' => array_search(Corrections::FAQ_PROJECT_DE, Corrections::EXACT, true)],
['question' => 'Eigene Frage', 'answer' => 'Eigene Antwort mit Aufmaß.'],
]]);
$origin = storedSection('home', 'origin_section', ['steps' => [
['description' => 'Im CABINET Store nehmen wir Maß, planen bis ins Detail und stimmen jede Front und jede Innenaufteilung mit Ihnen ab.'],
]]);
$detail = storedSection('magazin', 'detail', ['cta_title' => 'Entdecken Sie mehr über <span class="text-white font-medium">Immobilien-Investments</span> und das <span class="text-white font-medium">B2in-Ökosystem</span>.']);
$this->seed(Corrections::class);
$this->seed(Corrections::class);
$answers = array_column($faq->refresh()->getTranslation('value', 'de')['questions'], 'answer');
expect($answers)->toBe([Corrections::FAQ_CONSULTATION_DE, Corrections::FAQ_PROJECT_DE, 'Eigene Antwort mit Aufmaß.'])
->and(Corrections::FAQ_PROJECT_DE)->not->toContain('Eskal')->not->toContain('absolute')
->and($origin->refresh()->getTranslation('value', 'de')['steps'][0]['description'])->toContain('Nach Ihrer Zusage nehmen wir vor Ort exakt Maß.')
->and($detail->refresh()->getTranslation('value', 'de')['cta_title'])->toContain('Weitere Einblicke rund um Einrichtung, Planung und Projekte');
});
it('updates the CABINET process and the Otterpohl promise in the brands', function () {
$this->seed(CmsBrandSeeder::class);
$this->seed(\Database\Seeders\BrandPageTextSeeder::class);
$this->seed(\Database\Seeders\OtterpohlBrandSeeder::class);
$this->seed(Corrections::class);
$cabinet = json_encode(CmsBrand::where('slug', 'cabinet')->first()->only(['sections', 'services']), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$otterpohl = json_encode(CmsBrand::where('slug', 'tischlerei')->first()->sections, JSON_UNESCAPED_UNICODE);
expect($cabinet)->toContain('Angebotsannahme → Anzahlung → professionelles Aufmaß → Feinplanung / Fertigungsfreigabe')
->toContain('Am Anfang reicht das grobe Maß. Am Ende entscheidet der Millimeter.')
->toContain('Nach Angebotsannahme und Anzahlung erfolgt das professionelle Aufmaß bei Ihnen vor Ort.')
->not->toContain('Grundlage jeder Planung')
->and($otterpohl)->toContain('Das erhöht die Planungssicherheit deutlich.')
->not->toContain('absolute Planungssicherheit');
});
it('inserts the two Febrü slides at positions 4 and 9 without touching own slides', function () {
$defaults = __('b2in.themes.b2in.hero.slides', [], 'de');
$old = array_values(array_filter($defaults, fn ($s) => ! str_contains($s['image'], 'februe')));
$row = storedSection('home', 'hero', ['title' => 'Hero', 'slides' => $old], ['title' => 'Hero', 'slides' => [['image' => 'eigenes.webp']]]);
$this->seed(Corrections::class);
$this->seed(Corrections::class);
$value = $row->refresh()->getTranslations('value');
$images = array_column($value['de']['slides'], 'image');
expect($images)->toHaveCount(11)
->and($images[3])->toBe('b2in/slideshow/februe-active-2-0-buero.webp')
->and($images[8])->toBe('b2in/slideshow/februe-elements-soul-grossraum.webp')
->and(end($images))->toBe('b2in/slideshow/rotpunkt-bad-mk04.webp')
->and($value['de']['slides'][3]['label'])->toBe('Febrü')
->and(file_exists(public_path('img/assets/b2in/slideshow/februe-active-2-0-buero.webp')))->toBeTrue();
// Eigene Liste ohne die Nachbarbilder bleibt unangetastet.
expect($value['en']['slides'])->toBe([['image' => 'eigenes.webp']]);
});
it('replaces the former clubs once and keeps a list maintained since', function () {
$row = storedSection('home', 'sponsoring', ['logos' => [['name' => 'DSC Arminia Bielefeld'], ['name' => 'TC Hövelhof']]],
['logos' => [['name' => 'Own club']]]);
$this->seed(Corrections::class);
$value = $row->refresh()->getTranslations('value');
expect(array_column($value['de']['logos'], 'name'))->toBe(['DSC Arminia Bielefeld', 'TC Bad Salzuflen', 'TC Hiddenhausen', 'TC Blau Weiss Halle'])
->and($value['en']['logos'])->toBe([['name' => 'Own club']]);
});
it('rewrites the custom-made article, takes two articles offline and writes dates in German', function () {
$this->seed(\Database\Seeders\CmsArticleSeeder::class);
CmsArticle::query()->update(['date_label' => 'August 26, 2026', 'read_time' => '5 min read', 'is_published' => true]);
CmsArticle::where('slug', Corrections::REWRITTEN_ARTICLE)->first()
->setTranslation('title', 'de', 'Nach Maß: Warum der Millimeter über den Raum entscheidet')->save();
$this->seed(Corrections::class);
$article = CmsArticle::where('slug', Corrections::REWRITTEN_ARTICLE)->first();
expect($article->getTranslation('title', 'de'))->toContain('und am Anfang grobe Maße reichen')
->and($article->getTranslation('content', 'de')['sections'][0]['title'])->toBe('Erst die Idee, dann der Millimeter')
->and($article->date_label)->toBe('26. August 2026')
->and($article->read_time)->toBe('5 Min. Lesezeit')
->and(CmsArticle::whereIn('slug', Corrections::UNPUBLISHED_ARTICLES)->where('is_published', true)->count())->toBe(0);
});
it('converts dates and read times to German and leaves German ones alone', function (string $input, string $expected) {
expect(Corrections::germanDate($input))->toBe($expected);
})->with([
['August 26, 2026', '26. August 2026'],
['März 5, 2026', '5. März 2026'],
['26. August 2026', '26. August 2026'],
['Herbst 2026', 'Herbst 2026'],
]);
it('converts read times to German', function () {
expect(Corrections::germanReadTime('7 min read'))->toBe('7 Min. Lesezeit')
->and(Corrections::germanReadTime('7 Min. Lesezeit'))->toBe('7 Min. Lesezeit');
});
it('ships the corrected texts for fresh installations', function () {
$lang = file_get_contents(resource_path('lang/de/b2in.php'));
expect($lang)->toContain(Corrections::FAQ_CONSULTATION_DE)
->toContain(Corrections::FAQ_COST_DE)
->toContain(Corrections::FAQ_PROJECT_DE)
->not->toContain('seriöse Zahl')
->not->toContain('absolute Vertragssicherheit')
->not->toContain('B2in-Ökosystem')
->not->toContain('min read');
});
it('adds the club website to an already corrected list', function () {
$row = storedSection('home', 'sponsoring', ['logos' => [['name' => 'DSC Arminia Bielefeld'], ['name' => 'TC Hiddenhausen', 'url' => 'https://eigene.example']]]);
$this->seed(Corrections::class);
$logos = $row->refresh()->getTranslation('value', 'de')['logos'];
expect($logos[0]['url'])->toBe('https://www.arminia.de')
->and($logos[1]['url'])->toBe('https://eigene.example');
});