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