b2in/tests/Feature/Cms/CmsContentSeederTest.php
Kevin Adametz 6d53243ea6 B2in Interior: Relaunch der Website, Kernmarken-CMS und Terminanfrage
Umsetzung von AP0-AP6 aus dev/2026-08-28/entwicklungsplan.md sowie der
vorab gefundenen Befunde T1 und B1-B13.

Befunde
- CmsContentSeeder überschrieb bei jedem Lauf alle redaktionellen Inhalte;
  jetzt Redaktionsschutz plus gezielter Relaunch-Seeder (B1)
- Magazin-CMS war nicht ans Frontend angebunden, Artikel jetzt über Slugs
  mit 301 von den alten numerischen URLs (B2)
- Vier veröffentlichte Display-Playlists zeigten auf b2in.test und wären
  live nicht ladbar gewesen (B13)
- Info-Tablet speicherte geleerte Öffnungszeiten nicht (T1)
- /service und /portfolio lieferten seit Februar 500 (B12)

Website
- Startseite neu strukturiert und umgetextet, eigene Seite /marken mit
  Detailseite je Kernmarke, /netzwerk und Altrouten leiten dorthin
- Galerie je Marke, getrennt nach realisierten Projekten und
  Visualisierungen; ohne gesetzte Kategorie gilt ein Bild als Entwurf
- Seite /referenzen, gespeist aus den Markengalerien
- Showroom-Display im 9:16-Rahmen über eine schreibgeschützte Embed-Route
  ohne Preview-Token
- Terminanfrage-Widget auf allen Seiten, Markenauswahl aus dem CMS,
  WhatsApp per Klick-zum-Chat-Link mit vorbereitetem Text
- FAQ, Über-uns, Kontakt und Magazin inhaltlich auf Interior umgestellt

Sonstiges
- Vorschau-Instanzen liefern noindex und verlinken sich bei unbekanntem
  Host auf sich selbst statt auf eine fremde Domain
- Seeder-Zugangsdaten aus dem Code in die Konfiguration (B8)
- Ablauf für die Vorschau: dev/2026-08-28/preview-deployment.md

833 Tests grün (3084 Assertions), Pint sauber.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:31:19 +00:00

227 lines
7.5 KiB
PHP

<?php
declare(strict_types=1);
use FluxCms\Core\Models\CmsContent;
use FluxCms\Core\Services\CmsContentService;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('seeder creates content entries for all mapped sections', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
expect(CmsContent::count())->toBeGreaterThan(0);
$expectedGroups = ['global', 'home', 'immobilien', 'netzwerk', 'faq', 'contact', 'about', 'magazin'];
foreach ($expectedGroups as $group) {
expect(CmsContent::where('group', $group)->exists())
->toBeTrue("Expected group '{$group}' to exist in CmsContent");
}
});
test('seeder creates all home sections', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$homeKeys = CmsContent::where('group', 'home')->pluck('key')->toArray();
expect($homeKeys)
->toContain('hero')
->toContain('founder_bar')
->toContain('ecosystem_core')
->toContain('vision_section');
});
test('seeder stores hero content as translatable json', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$hero = CmsContent::where('group', 'home')->where('key', 'hero')->first();
expect($hero)->not->toBeNull()
->and($hero->type)->toBe('json');
$valueDe = $hero->getTranslation('value', 'de');
$valueEn = $hero->getTranslation('value', 'en');
expect($valueDe)->toBeArray()
->toHaveKeys(['title', 'subtitle', 'image']);
expect($valueEn)->toBeArray()
->toHaveKeys(['title', 'subtitle', 'image']);
});
test('cms helper returns correct content after seeding', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$hero = cms('home.hero');
expect($hero)->toBeArray()
->and($hero['title'])->toContain('B2in');
});
test('cms content service getGroup returns all section keys', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$service = app(CmsContentService::class);
$homeGroup = $service->getGroup('home');
expect($homeGroup)->toBeArray()
->toHaveKey('hero')
->toHaveKey('founder_bar')
->toHaveKey('ecosystem_core');
expect($homeGroup['hero'])->toBeArray()
->toHaveKey('title');
});
test('seeder is idempotent', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$countFirst = CmsContent::count();
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$countSecond = CmsContent::count();
expect($countSecond)->toBe($countFirst);
});
test('immobilien sections are seeded correctly', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$immoKeys = CmsContent::where('group', 'immobilien')->pluck('key')->toArray();
expect($immoKeys)
->toContain('hero_v2')
->toContain('warum_dubai')
->toContain('kaufprozess')
->toContain('bruecke')
->toContain('mindset')
->toContain('moebel_vorteil');
});
test('faq content contains questions array', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$faq = cms('faq.faq');
expect($faq)->toBeArray()
->toHaveKey('questions')
->and($faq['questions'])->toBeArray()
->not->toBeEmpty();
});
test('cms_theme_section returns CMS content after seeding', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$hero = cms_theme_section('hero');
expect($hero)->toBeArray()
->and($hero['title'])->toContain('B2in');
});
test('portfolio is stored under immobilien group for CMS', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$portfolio = CmsContent::where('group', 'immobilien')->where('key', 'portfolio')->first();
expect($portfolio)->not->toBeNull()
->and($portfolio->type)->toBe('json');
$valueDe = $portfolio->getTranslation('value', 'de');
expect($valueDe)->toBeArray()
->toHaveKey('projects')
->and($valueDe['projects'])->toBeArray();
});
// ========================================
// Redaktionsschutz
// ========================================
test('seeder keeps content that was edited in the admin', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$hero = CmsContent::where('group', 'home')->where('key', 'hero')->firstOrFail();
$edited = $hero->getTranslation('value', 'de');
$edited['title'] = 'Von Hand im CMS gepflegt';
$hero->setTranslation('value', 'de', $edited);
$hero->save();
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$hero->refresh();
expect($hero->getTranslation('value', 'de')['title'])->toBe('Von Hand im CMS gepflegt');
});
test('seeder still creates sections that are missing', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
CmsContent::where('group', 'home')->where('key', 'hero')->delete();
$this->seed(\Database\Seeders\CmsContentSeeder::class);
expect(CmsContent::where('group', 'home')->where('key', 'hero')->exists())->toBeTrue();
});
test('seeder overwrites existing content when explicitly told to', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$hero = CmsContent::where('group', 'home')->where('key', 'hero')->firstOrFail();
$edited = $hero->getTranslation('value', 'de');
$edited['title'] = 'Von Hand im CMS gepflegt';
$hero->setTranslation('value', 'de', $edited);
$hero->save();
$seeder = new \Database\Seeders\CmsContentSeeder;
$seeder->overwriteExisting = true;
$seeder->run();
$hero->refresh();
expect($hero->getTranslation('value', 'de')['title'])->not->toBe('Von Hand im CMS gepflegt');
});
// ========================================
// Gezielter Relaunch-Lauf
// ========================================
test('relaunch seeder overwrites only the sections it lists', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
// Eine Relaunch-Sektion und eine, die nicht in der Liste steht.
$hero = CmsContent::where('group', 'home')->where('key', 'hero')->firstOrFail();
$hero->setTranslation('value', 'de', ['title' => 'Alter Stand aus dem CMS']);
$hero->save();
$magazin = CmsContent::where('group', 'home')->where('key', 'ecosystem_core')->firstOrFail();
$magazin->setTranslation('value', 'de', ['title' => 'Von Hand gepflegt']);
$magazin->save();
$this->seed(\Database\Seeders\InteriorRelaunchCmsSeeder::class);
$hero->refresh();
$magazin->refresh();
expect($hero->getTranslation('value', 'de')['title'])->toContain('B2in Interior');
expect($magazin->getTranslation('value', 'de')['title'])->toBe('Von Hand gepflegt');
});
test('relaunch seeder creates the new sections when they are missing', function () {
$this->seed(\Database\Seeders\InteriorRelaunchCmsSeeder::class);
foreach ([['home', 'audience_switch'], ['home', 'core_brands'], ['home', 'sponsoring'], ['home', 'immobilien_teaser'], ['global', 'footer']] as [$group, $key]) {
expect(CmsContent::where('group', $group)->where('key', $key)->exists())
->toBeTrue("Sektion fehlt: {$group}.{$key}");
}
});
test('onlySections leaves every other section untouched', function () {
$this->seed(\Database\Seeders\CmsContentSeeder::class);
$before = CmsContent::count();
$seeder = new \Database\Seeders\CmsContentSeeder;
$seeder->onlySections = ['hero'];
$seeder->overwriteExisting = true;
$seeder->run();
expect(CmsContent::count())->toBe($before);
});