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

245 lines
9.7 KiB
PHP
Raw 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 Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
// ========================================
// Neue Reihenfolge der Startseite
// ========================================
it('renders the homepage sections in the order from the concept', function () {
$html = $this->get('/')->assertSuccessful()->getContent();
$order = [
'Räume aus einer Hand.', // Hero
'B2in by Marcel Scheibe', // Marcel-Badge
'Einrichtung für Ihr Zuhause', // Weiche Privat/Geschäft (Karte 1)
'Unsere <span class="text-secondary">Kernmarken', // Kernmarken
'Expertise und Vertrauen</span>', // Über Marcel
'Regional <span class="text-secondary">verbunden', // Sponsoring
'Internationale Immobilien', // Immobilien-Teaser
'Ihr nächster', // Abschluss-CTA
];
$lastPosition = -1;
foreach ($order as $needle) {
$position = strpos($html, $needle);
expect($position)->not->toBeFalse("Sektion nicht gefunden: {$needle}");
expect($position)->toBeGreaterThan($lastPosition, "Falsche Reihenfolge bei: {$needle}");
$lastPosition = $position;
}
});
it('no longer shows the three-pillar ecosystem section', function () {
$this->get('/')
->assertSuccessful()
->assertDontSee('Ein Ökosystem')
->assertDontSee('Local-for-Local Marktplatz')
->assertDontSee('Supply-Chain-Management');
});
it('no longer shows the old two-worlds section', function () {
$this->get('/')
->assertSuccessful()
->assertDontSee('Zwei Welten');
});
// ========================================
// Hero
// ========================================
it('offers a single hero button since the review on 1 October 2026', function () {
$html = $this->get('/')->assertSuccessful()->getContent();
$hero = substr($html, strpos($html, 'id="hero"'), 6000);
expect($hero)->toContain('Unsere Marken entdecken')
->toContain('<span class="text-secondary">Räume aus einer Hand.</span><br>Mit starken Marken und persönlichen Lösungen.')
->not->toContain('btn-secondary-accent')
->not->toContain('Termin anfragen');
});
it('shows the three hero checkmarks', function () {
$this->get('/')
->assertSuccessful()
->assertSee('Deutsche Manufakturen')
->assertSee('Planung bis Montage')
->assertSee('Zwei Showrooms in Bielefeld');
});
// ========================================
// Kernmarken
// ========================================
it('shows all three core brands with an anchor the navigation can reach', function () {
$this->get('/')
->assertSuccessful()
->assertSee('id="marken"', false)
->assertSee('CABINET')
->assertSee('Rotpunkt Küchen', false)
->assertSee('Febrü', false);
});
it('describes Febrü with the home office first', function () {
$html = $this->get('/')->assertSuccessful()->getContent();
$homeOffice = strpos($html, 'Homeoffice');
$office = strpos($html, 'Büroeinrichtung');
expect($homeOffice)->not->toBeFalse();
expect($office)->not->toBeFalse();
expect($homeOffice)->toBeLessThan($office);
});
// ========================================
// Navigation
// ========================================
it('leads the interior navigation with brands and leaves real estate to the world switcher', function () {
$html = $this->get('/')->assertSuccessful()->getContent();
$navStart = (int) strpos($html, 'aria-label="Hauptnavigation"');
$nav = substr($html, $navStart, (int) strpos($html, '</nav>', $navStart) - $navStart);
expect($nav)->toContain('Marken');
expect($nav)->toContain('/marken');
// „Immobilien“ ist seit der Welten-Trennung kein Navigationspunkt mehr – der Wechsler
// in Kopf- und Fusszeile ist der einzige Weg in die Immobilien-Welt.
expect($nav)->not->toContain('href="/immobilien"');
expect($nav)->not->toContain('Netzwerk');
expect(strpos($nav, 'Marken'))->toBeLessThan(strpos($nav, 'Magazin'));
});
it('sends the retired network page to the brands page', function () {
$this->get('/netzwerk')->assertRedirect('/marken');
});
it('teases the brands on the homepage and links to the full overview', function () {
$this->get('/')
->assertSuccessful()
->assertSee('Alle Marken ansehen')
->assertSee('/marken');
});
// ========================================
// SEO
// ========================================
it('carries the new title and description', function () {
$this->get('/')
->assertSuccessful()
->assertSee('B2in Interior – Premium-Einrichtung', false)
->assertDontSee('Connecting Design and Property');
});
// ========================================
// Sponsoring
// ========================================
it('shows the four supported clubs with their logos (08.10.2026)', function () {
$this->get('/')
->assertSuccessful()
->assertSee('sponsoring/arminia-bielefeld.png')
->assertSee('sponsoring/tc-bad-salzuflen.png')
->assertSee('sponsoring/tc-hiddenhausen.png')
->assertSee('sponsoring/tc-blau-weiss-halle.jpg')
->assertDontSee('Waldliesborn')
->assertDontSee('Hövelhof');
foreach (['tc-bad-salzuflen.png', 'tc-hiddenhausen.png', 'tc-blau-weiss-halle.jpg'] as $file) {
expect(file_exists(public_path("img/assets/sponsoring/{$file}")))->toBeTrue();
}
});
it('puts every sponsor logo on a white stage inside its own tile', function () {
$html = $this->get('/')->assertSuccessful()->getContent();
expect(substr_count($html, 'bg-white flex items-center justify-center h-36'))->toBe(4);
});
it('names each club with the same plain line', function () {
$this->get('/')
->assertSuccessful()
->assertSee('DSC Arminia Bielefeld')
->assertSee('TC Bad Salzuflen')
->assertSee('TC Hiddenhausen')
->assertSee('TC Blau Weiss Halle')
->assertSee('Tennisclub aus Halle (Westfalen) – regional verwurzelt und engagiert im Vereinssport.', false);
});
it('links every club to its website in a new tab', function () {
$html = $this->get('/')->assertSuccessful()->getContent();
$section = substr($html, (int) strpos($html, 'id="engagement"'), 8000);
expect($section)->toContain('href="https://www.arminia.de" target="_blank" rel="noopener"')
->toContain('href="https://www.tcbadsalzuflen.de/" target="_blank" rel="noopener"')
->toContain('href="https://tc-hiddenhausen.de/"')
->toContain('href="https://www.tcbw-hallewestf.de/Home"')
->and(substr_count($section, 'target="_blank"'))->toBe(4);
});
// ========================================
// Logo mit Zusatz „Interior"
// ========================================
it('uses the interior logo in the header and footer', function () {
// Beide Male die positive Variante: der Interior-Footer liegt auf Sand, nicht auf dunklem Grund.
$html = $this->get('/')->assertSuccessful()->getContent();
expect(substr_count($html, 'b2in-interior-logo-positive.svg'))->toBeGreaterThanOrEqual(2);
expect($html)->not->toContain('img/logos/b2in-logo-positive.svg');
});
it('ships the interior logo files in both variants', function () {
foreach (['positive', 'negative'] as $variant) {
expect(file_exists(public_path("img/logos/b2in-interior-logo-{$variant}.svg")))->toBeTrue();
expect(file_exists(public_path("img/logos/b2in-interior-logo-{$variant}.png")))->toBeTrue();
}
});
it('crops the logo viewBox to the artwork', function () {
// Die Lieferung lag auf einem A4-Artboard mit 74 % Weissraum. Ungeschnitten wuerde das
// Logo bei gleicher CSS-Hoehe rund halb so gross rendern wie das bisherige.
// Zuschnitt der Fassung vom 07.09.2026 (Tanne/Ocker): Inhalt 153,3–440,5 × 95,3–320,8.
$svg = file_get_contents(public_path('img/logos/b2in-interior-logo-positive.svg'));
expect($svg)->toContain('viewBox="152.5 94.5 289 228"');
expect($svg)->not->toContain('595.3 419.5');
});
it('puts the vision section in a white box on the ochre surface with a smaller portrait (07.10.2026)', function () {
$html = $this->get('/')->assertSuccessful()->getContent();
$vision = substr($html, (int) strpos($html, 'id="vision"') - 400, 3000);
expect($vision)->toMatch('/id="vision" class="section-padding bg-secondary"/')
->toContain('data-vision-box')
->toContain('rounded-3xl bg-white')
->toContain('lg:col-span-4')
->toContain('aspect-[4/5]')
->toContain('Expertise und Vertrauen');
});
it('keeps the open vision layout for the other themes', function () {
\Livewire\Livewire::test(\App\Livewire\Web\Components\Sections\VisionSection::class)
->assertSet('boxed', false)
->assertDontSeeHtml('data-vision-box');
});
it('highlights "Expertise und Vertrauen" in the accent colour inside the white box', function () {
\FluxCms\Core\Models\CmsContent::create(['group' => 'home', 'key' => 'vision_section', 'type' => 'json', 'order' => 0,
'value' => ['de' => array_merge(__('b2in.themes.b2in.vision_section', [], 'de'), ['title' => 'Gebaut auf Expertise und Vertrauen'])]]);
$this->seed(\Database\Seeders\HomeVisionTitleSeeder::class);
$this->seed(\Database\Seeders\HomeVisionTitleSeeder::class);
$html = $this->get('/')->assertSuccessful()->getContent();
expect($html)->toContain('<h2 class="text-section-title">Gebaut auf <span class="text-secondary">Expertise und Vertrauen</span></h2>')
->and(file_get_contents(resource_path('css/web/theme-b2in.css')))
->toContain("section.bg-secondary [data-vision-box] .text-secondary {\n color: hsl(var(--secondary));");
});