b2in/tests/Feature/MarkenPageTest.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

195 lines
6.6 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
use App\Models\CmsBrand;
use FluxCms\Core\Models\CmsContent;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
/**
* Sichtbares Markup ohne Livewire-Snapshots.
*
* wire:snapshot serialisiert die Komponentendaten als JSON ins Attribut. Umlaute werden dort
* zu \uXXXX escaped, ASCII-Namen wie "CABINET" nicht – Positionsvergleiche auf dem rohen HTML
* messen sonst den Snapshot statt des sichtbaren Textes.
*/
function markenBody(string $html): string
{
$html = preg_replace('/wire:snapshot="[^"]*"/', '', $html) ?? $html;
return substr($html, (int) strpos($html, '<main'));
}
beforeEach(function () {
$this->seed(\Database\Seeders\CmsBrandSeeder::class);
});
// ========================================
// Aufbau laut unterseite_marke.md
// ========================================
it('renders the page sections in the order from the spec', function () {
$html = markenBody($this->get('/marken')->assertSuccessful()->getContent());
$order = [
'Unsere <span class="text-secondary">Marken', // 1. Seitenkopf
'CABINET', // 2. Kernmarken
'Rotpunkt Küchen',
'Febrü',
'unser Netzwerk', // 3. Partnernetzwerk
'Markenwelt wächst', // 4. Marken werden ergänzt
'Lernen Sie unsere', // 5. Abschluss-CTA
];
$last = -1;
foreach ($order as $needle) {
$pos = strpos($html, $needle);
expect($pos)->not->toBeFalse("Nicht gefunden: {$needle}");
expect($pos)->toBeGreaterThan($last, "Falsche Reihenfolge bei: {$needle}");
$last = $pos;
}
});
it('shows no breadcrumb in the page hero (07.10.2026)', function () {
$this->get('/marken')
->assertSuccessful()
->assertDontSee('aria-label="Breadcrumb"', false);
});
it('carries the title and description from the spec', function () {
$this->get('/marken')
->assertSuccessful()
->assertSee('Unsere Marken – CABINET, Rotpunkt Küchen, Febrü &amp; Otterpohl | B2in Interior', false)
->assertSee('Ausgewählte Hersteller statt beliebiger Kataloge', false);
});
// ========================================
// Marken-Abschnitt
// ========================================
it('shows the longer text and the three highlights per brand', function () {
$this->get('/marken')
->assertSuccessful()
->assertSee('Wo Standardmaße an ihre Grenzen stoßen', false)
->assertSee('Millimetergenaue Planung')
->assertSee('Individuelle Fronten und Innenausstattung')
->assertSee('Persönliche Beratung vor Ort', false);
});
it('links every brand to its detail page', function () {
$this->get('/marken')
->assertSuccessful()
->assertSee('/marken/cabinet')
->assertSee('/marken/rotpunkt')
->assertSee('/marken/februe');
});
it('keeps the brand order from the CMS', function () {
$headings = function (string $html): array {
preg_match_all('#wire:key="brand-([a-z-]+)"#', $html, $matches);
return $matches[1];
};
expect($headings($this->get('/marken')->assertSuccessful()->getContent()))
->toBe(['cabinet', 'rotpunkt', 'februe', 'tischlerei']);
CmsBrand::where('slug', 'februe')->update(['order' => 0]);
expect($headings($this->get('/marken')->getContent()))
->toBe(['februe', 'cabinet', 'rotpunkt', 'tischlerei']);
});
it('hides unpublished brands', function () {
CmsBrand::where('slug', 'februe')->update(['is_published' => false]);
$this->get('/marken')
->assertSuccessful()
->assertSee('CABINET')
->assertDontSee('/marken/februe');
});
// ========================================
// WhatsApp
// ========================================
it('hides the whatsapp button while no number is stored', function () {
// Lieber kein Button als einer auf eine erfundene Nummer.
$this->get('/marken')
->assertSuccessful()
->assertSee('Termin anfragen')
->assertDontSee('wa.me');
});
it('shows the whatsapp button once a number is stored', function () {
CmsContent::create([
'group' => 'global',
'key' => 'contact',
'type' => 'json',
'value' => ['de' => ['whatsapp' => '+49 151 51002992']],
'order' => 0,
]);
$this->get('/marken')
->assertSuccessful()
->assertSee('https://wa.me/4915151002992')
->assertSee('Per WhatsApp schreiben');
});
// ========================================
// Abgrenzung der Blöcke
// ========================================
it('sets the page header on the same offset surface as the other heroes', function () {
$html = markenBody($this->get('/marken')->assertSuccessful()->getContent());
// Dieselbe Fläche wie Startseite und Netzwerkseite.
expect($html)->toContain('bg-hero-container');
});
it('frames every brand block so it reads as one unit', function () {
$html = markenBody($this->get('/marken')->assertSuccessful()->getContent());
expect(substr_count($html, 'card-elevated rounded-3xl'))->toBe(CmsBrand::query()->published()->count());
});
it('places a smaller image beside the page header text', function () {
$html = markenBody($this->get('/marken')->assertSuccessful()->getContent());
// Text 3, Bild 2 Spalten – das Bild begleitet, es dominiert nicht.
expect($html)->toContain('lg:col-span-3')
->and($html)->toContain('lg:col-span-2');
});
it('leaves out the image frame entirely when no motif is maintained', function () {
CmsContent::create([
'group' => 'marken',
'key' => 'hero',
'type' => 'json',
'value' => ['de' => ['title' => 'Unsere Marken', 'text' => 'Kurztext.', 'image' => '']],
'order' => 0,
]);
$html = markenBody($this->get('/marken')->assertSuccessful()->getContent());
expect($html)->toContain('Unsere Marken')
->and($html)->not->toContain('lg:col-span-2');
});
it('sets the brand blocks off from the origin section with an image break (07.10.2026)', function () {
$html = $this->get('/marken')->assertSuccessful()->getContent();
$image = strpos($html, 'id="bild"');
$origin = strpos($html, 'id="herkunft"');
expect($image)->not->toBeFalse()
->and($origin)->toBeGreaterThan($image)
->and(substr($html, $image, 2500))->toContain('slideshow/rotpunkt-bad-mk04.webp')
->toContain('Hinter jeder unserer Marken stehe ich persönlich. Ich kenne die Werke und die Menschen, die dort arbeiten.')
->toContain('– Marcel Scheibe');
expect(config('cms_section_map.sections.marken_image_break'))->toBe(['marken', 'image_break']);
});