b2in/tests/Feature/BrandPageTest.php
Kevin Adametz c8899e2d7d Markenlogos einbinden: Seitenkopf und alle Kacheln
Zulieferung dev/2026-08-28/markenlogos/ (CABINET und Rotpunkt als SVG, Februe
als PNG). Die Dateien liegen unter public/img/assets/marken/ und haengen als
`logo` an der jeweiligen Marke.

- Kopf der Markenseite: Logo anstelle des Schriftzugs, im <h1>, damit die
  Ueberschrift erhalten bleibt – den Text liefert das alt-Attribut. Ohne Logo
  erscheint weiterhin der Schriftzug.
- Logo zusaetzlich auf der Startseiten-Kachel, im Block auf /marken und in den
  „Weitere Marken“-Kacheln am Seitenfuss.
- Feste Box (Hoehe + Maximalbreite + object-contain) statt fester Hoehe: die
  Seitenverhaeltnisse gehen weit auseinander (Februe 2,3:1, Rotpunkt 1,2:1),
  mit reiner Hoehenangabe waere Februe doppelt so breit erschienen. Konvention
  aus der Sponsoring-Sektion.
- Februe-Logo auf seinen Inhalt beschnitten: im Original waren knapp drei
  Viertel der Flaeche leerer Rand, in einer Box haette es winzig gewirkt.
  53 KB → 15 KB.
- BrandLogoSeeder traegt die Logos auf bestehenden Installationen nach –
  CmsBrandSeeder ueberspringt vorhandene Marken. Setzt nur `logo` und nur bei
  leerem Feld, ein im Admin hochgeladenes Logo bleibt stehen.

Tests: 871 gruen (3215 Assertions), Pint sauber, alle Themes gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 11:16:41 +00:00

277 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 App\Models\CmsBrand;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->seed(\Database\Seeders\CmsBrandSeeder::class);
});
// ========================================
// Kacheln auf der Startseite
// ========================================
it('shows the maintained brands on the homepage', function () {
$this->get('/')
->assertSuccessful()
->assertSee('CABINET')
->assertSee('Rotpunkt Küchen', false)
->assertSee('Febrü', false);
});
it('links each tile to its detail page', function () {
$this->get('/')
->assertSuccessful()
->assertSee('/marken/cabinet')
->assertSee('/marken/rotpunkt')
->assertSee('/marken/februe');
});
it('hides unpublished brands from the homepage', function () {
CmsBrand::where('slug', 'februe')->update(['is_published' => false]);
$this->get('/')
->assertSuccessful()
->assertSee('CABINET')
->assertDontSee('/marken/februe');
});
// ========================================
// Detailseite
// ========================================
it('serves a detail page for every brand', function () {
foreach (['cabinet', 'rotpunkt', 'februe'] as $slug) {
$this->get('/marken/'.$slug)->assertSuccessful();
}
});
it('renders the template sections from the concept', function () {
$this->get('/marken/cabinet')
->assertSuccessful()
->assertSee('CABINET') // Header
->assertSee('Einbauschränke nach Maß', false) // Claim
->assertSee('Showroom Bielefeld')
->assertSee('Leistungen und Besonderheiten') // Leistungen
->assertSee('Aufmaß vor Ort', false)
->assertSee('Termin anfragen'); // CTA
});
it('links to the other brands from a detail page', function () {
$this->get('/marken/cabinet')
->assertSuccessful()
->assertSee('/marken/rotpunkt')
->assertSee('/marken/februe');
});
it('returns 404 for an unknown brand', function () {
$this->get('/marken/gibt-es-nicht')->assertNotFound();
});
it('returns 404 for an unpublished brand', function () {
CmsBrand::where('slug', 'februe')->update(['is_published' => false]);
$this->get('/marken/februe')->assertNotFound();
});
it('carries a brand specific title and description', function () {
$this->get('/marken/cabinet')
->assertSuccessful()
->assertSee('CABINET – Einbauschränke nach Maß. | B2in Interior', false)
->assertDontSee('Connecting Design and Property');
});
// ========================================
// CMS-Anbindung
// ========================================
it('renders what is maintained in the CMS', function () {
$cabinet = CmsBrand::where('slug', 'cabinet')->firstOrFail();
$cabinet->setTranslation('tagline', 'de', 'Im CMS geänderter Claim');
$cabinet->save();
$this->get('/')->assertSuccessful()->assertSee('Im CMS geänderter Claim');
$this->get('/marken/cabinet')->assertSuccessful()->assertSee('Im CMS geänderter Claim');
});
it('picks up a brand that was added in the CMS, without a code change', function () {
CmsBrand::factory()->create([
'slug' => 'neue-marke',
'name' => ['de' => 'Neue Marke', 'en' => 'New Brand'],
'order' => 9,
]);
$this->get('/')->assertSuccessful()->assertSee('Neue Marke');
$this->get('/marken/neue-marke')->assertSuccessful()->assertSee('Neue Marke');
});
// ========================================
// Galerie: realisierte Projekte vs. Inspirationen
// ========================================
it('shows no gallery section while no images are maintained', function () {
$this->get('/marken/cabinet')
->assertSuccessful()
->assertDontSee('Referenzen und Inspirationen');
});
it('shows both groups with their own labels', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update([
'gallery' => [
['image' => 'projekt.webp', 'category' => 'project', 'caption' => 'Ankleide in Bielefeld'],
['image' => 'entwurf.webp', 'category' => 'inspiration', 'caption' => 'Entwurf Garderobe'],
],
]);
$this->get('/marken/cabinet')
->assertSuccessful()
->assertSee('Referenzen und Inspirationen')
->assertSee('Realisierte Projekte')
->assertSee('Inspirationen und Entwürfe', false)
->assertSee('Realisiertes Projekt')
->assertSee('Visualisierung')
->assertSee('Ankleide in Bielefeld')
->assertSee('Entwurf Garderobe');
});
it('does not offer a tab switch when only one group is filled', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update([
'gallery' => [
['image' => 'projekt.webp', 'category' => 'project', 'caption' => 'Nur ein Projekt'],
],
]);
$this->get('/marken/cabinet')
->assertSuccessful()
->assertSee('Realisierte Projekte')
->assertDontSee('Inspirationen und Entwürfe', false);
});
it('marks an image without a category as visualisation, never as a completed project', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update([
'gallery' => [
['image' => 'ohne-kategorie.webp'],
],
]);
$this->get('/marken/cabinet')
->assertSuccessful()
->assertSee('Visualisierung')
->assertDontSee('Realisiertes Projekt');
});
it('still separates inspirations from completed projects after the rewording', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update([
'gallery' => [
['image' => 'entwurf.webp', 'category' => 'inspiration'],
],
]);
$this->get('/marken/cabinet')
->assertSuccessful()
->assertSee('Entwürfe und Visualisierungen', false)
->assertSee('Visualisierung')
->assertDontSee('Realisiertes Projekt');
});
it('fills the demo galleries with both categories', function () {
$this->seed(\Database\Seeders\DemoBrandGallerySeeder::class);
$grouped = CmsBrand::groupGallery(CmsBrand::where('slug', 'cabinet')->firstOrFail()->gallery);
expect($grouped['projects'])->not->toBeEmpty()
->and($grouped['inspirations'])->not->toBeEmpty();
// Der Vermerk „BEISPIEL“ ist auf Kundenwunsch entfallen. Die Unterscheidung muss deshalb
// an der Kategorie haengen – jedes Demobild braucht eine ausdrueckliche.
foreach (CmsBrand::all() as $brand) {
foreach ($brand->gallery ?? [] as $item) {
expect($item['caption'])->not->toContain('BEISPIEL')
->and($item['category'])->toBeIn(CmsBrand::GALLERY_CATEGORIES);
}
}
});
it('strips the old example prefix without touching image, category or order', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update([
'gallery' => [
['image' => 'a.webp', 'category' => 'project', 'caption' => 'BEISPIEL – Ankleide, Bielefeld'],
['image' => 'b.webp', 'category' => 'inspiration', 'caption' => 'Entwurf Garderobe'],
],
]);
$this->seed(\Database\Seeders\BrandGalleryCaptionNormalizerSeeder::class);
$gallery = CmsBrand::where('slug', 'cabinet')->firstOrFail()->gallery;
expect($gallery)->toHaveCount(2)
->and($gallery[0]['caption'])->toBe('Ankleide, Bielefeld')
->and($gallery[0]['image'])->toBe('a.webp')
->and($gallery[0]['category'])->toBe('project')
->and($gallery[1]['caption'])->toBe('Entwurf Garderobe');
});
// ========================================
// Markenlogos
// ========================================
it('shows the logo instead of the wordmark at the top of a brand page', function () {
$html = $this->get('/marken/rotpunkt')->assertSuccessful()->getContent();
// Das Logo sitzt im <h1>, damit die Ueberschrift erhalten bleibt.
expect($html)->toMatch('/<h1>\s*<img[^>]*marken\/rotpunkt\.svg/')
->and($html)->toContain('alt="Rotpunkt Küchen"');
});
it('falls back to the wordmark for a brand without a logo', function () {
CmsBrand::where('slug', 'rotpunkt')->firstOrFail()->update(['logo' => '']);
$this->get('/marken/rotpunkt')
->assertSuccessful()
->assertSee('<h1 class="text-section-title">Rotpunkt Küchen</h1>', false);
});
it('shows the logo on every tile that leads to a brand page', function (string $path) {
$html = $this->get($path)->assertSuccessful()->getContent();
foreach (['cabinet.svg', 'rotpunkt.svg', 'februe.png'] as $file) {
expect($html)->toContain('marken/'.$file);
}
})->with([
'Startseite' => ['/'],
'Markenuebersicht' => ['/marken'],
]);
it('shows the logo on the other-brands tiles of a detail page', function () {
$html = $this->get('/marken/rotpunkt')->assertSuccessful()->getContent();
// Auf der Rotpunkt-Seite verweisen die Kacheln auf CABINET und Februe.
expect($html)->toContain('marken/cabinet.svg')
->and($html)->toContain('marken/februe.png');
});
it('ships the logo files the seeder points at', function () {
foreach (['cabinet.svg', 'rotpunkt.svg', 'februe.png'] as $file) {
expect(public_path('img/assets/marken/'.$file))->toBeFile();
}
});
it('keeps a logo that was uploaded in the admin', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update(['logo' => 'eigenes-logo.svg']);
$this->seed(\Database\Seeders\BrandLogoSeeder::class);
expect(CmsBrand::where('slug', 'cabinet')->firstOrFail()->logo)->toBe('eigenes-logo.svg');
});
it('adds the logo to a brand that has none yet', function () {
CmsBrand::where('slug', 'cabinet')->firstOrFail()->update(['logo' => '']);
$this->seed(\Database\Seeders\BrandLogoSeeder::class);
expect(CmsBrand::where('slug', 'cabinet')->firstOrFail()->logo)->toBe('marken/cabinet.svg');
});