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>
245 lines
9.7 KiB
PHP
245 lines
9.7 KiB
PHP
<?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));");
|
||
});
|