261 lines
10 KiB
PHP
261 lines
10 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Livewire\Web\Components\Sections\HeroSlider;
|
|
use App\Models\CmsBrand;
|
|
use Database\Seeders\AudienceSwitchProjectTextSeeder;
|
|
use Database\Seeders\BrandImageSeeder;
|
|
use Database\Seeders\CmsBrandSeeder;
|
|
use FluxCms\Core\Models\CmsContent;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
function heroSection(string $html): string
|
|
{
|
|
$start = strpos($html, 'id="hero"');
|
|
$end = strpos($html, '</section>', $start);
|
|
|
|
return substr($html, $start, $end - $start);
|
|
}
|
|
|
|
// ========================================
|
|
// Entwurf /entwurf (Nachtrag 19)
|
|
// ========================================
|
|
|
|
it('renders the draft homepage in the greige palette with the gold logos', function () {
|
|
$html = $this->get('/entwurf')->assertSuccessful()->getContent();
|
|
|
|
expect($html)->toMatch('/<body class="[^"]*world-interior palette-greige"/')
|
|
->toContain('b2in-interior-logo-gold-positive.svg')
|
|
->toContain('b2in-interior-logo-gold-negative.svg')
|
|
->not->toContain('b2in-interior-logo-positive.svg')
|
|
->toContain('<meta name="robots" content="noindex, nofollow">');
|
|
});
|
|
|
|
it('keeps the regular homepage on the ocher logo and without the palette class', function () {
|
|
$html = $this->get('/')->assertSuccessful()->getContent();
|
|
|
|
expect($html)->not->toContain('palette-greige')
|
|
->not->toContain('logo-gold')
|
|
->toContain('b2in-interior-logo-positive.svg');
|
|
});
|
|
|
|
it('shows the new headline with one button and no announcement bar', function () {
|
|
$html = $this->get('/entwurf')->assertSuccessful()->getContent();
|
|
$hero = heroSection($html);
|
|
|
|
expect($hero)->toContain('<em>Räume aus einer Hand.</em> Mit starken Marken und persönlichen Lösungen.')
|
|
->toContain('B2in Interior')
|
|
->toContain('Unsere Marken entdecken')
|
|
->toContain('href="/marken"')
|
|
->not->toContain('Termin anfragen');
|
|
|
|
// Ankündigungsleiste bleibt auf der regulären Startseite, fehlt im Entwurf.
|
|
expect($this->get('/')->getContent())->toContain('Rotpunkt Küchen jetzt bei B2in Interior');
|
|
expect($html)->not->toContain('Rotpunkt Küchen jetzt bei B2in Interior');
|
|
|
|
// Das Termin-Widget unten rechts bleibt.
|
|
expect($html)->toContain('appointment-toggle');
|
|
});
|
|
|
|
it('keeps the remaining homepage sections below the new hero', function () {
|
|
$html = $this->get('/entwurf')->assertSuccessful()->getContent();
|
|
|
|
$order = ['id="hero"', 'B2in by Marcel Scheibe', 'Einrichtung für Ihr Zuhause', 'Unsere <span class="text-secondary">Kernmarken', 'id="footer"'];
|
|
$last = -1;
|
|
|
|
foreach ($order as $needle) {
|
|
$pos = strpos($html, $needle);
|
|
expect($pos)->not->toBeFalse("fehlt: {$needle}")->and($pos)->toBeGreaterThan($last);
|
|
$last = $pos;
|
|
}
|
|
});
|
|
|
|
it('keeps the greige draft free of the modern class and the switch', function () {
|
|
$html = $this->get('/entwurf')->assertSuccessful()->getContent();
|
|
|
|
expect($html)->not->toContain('class="palette-modern"')
|
|
->not->toContain('data-palette-switch');
|
|
});
|
|
|
|
it('can be switched off for the live site', function () {
|
|
config(['app.home_draft' => false]);
|
|
|
|
$this->get('/entwurf')->assertNotFound();
|
|
});
|
|
|
|
// ========================================
|
|
// Slideshow
|
|
// ========================================
|
|
|
|
it('runs the brand title images as slides while no slides are maintained', function () {
|
|
$this->seed(CmsBrandSeeder::class);
|
|
$this->seed(BrandImageSeeder::class);
|
|
|
|
$slides = Livewire::test(HeroSlider::class)->get('slides');
|
|
$count = CmsBrand::query()->published()->get()->filter(fn (CmsBrand $b) => trim((string) $b->image) !== '')->count();
|
|
|
|
expect(array_slice(array_column($slides, 'label'), 0, 3))->toBe(['CABINET', 'Rotpunkt Küchen', 'Febrü'])
|
|
->and($slides)->toHaveCount($count)
|
|
->and($slides[0]['image'])->toBe('marken/cabinet/ankleide-solitaer-eiche-tano.webp');
|
|
|
|
$html = $this->get('/entwurf')->assertSuccessful()->getContent();
|
|
$hero = heroSection($html);
|
|
|
|
expect(substr_count($hero, 'data-hero-slide='))->toBe($count)
|
|
->and(substr_count($hero, 'role="tab"'))->toBe($count)
|
|
->and($hero)->toContain('marken/rotpunkt/kueche-01.webp')
|
|
->toContain('aria-roledescription="carousel"');
|
|
});
|
|
|
|
it('skips brands without a title image', function () {
|
|
$this->seed(CmsBrandSeeder::class);
|
|
CmsBrand::query()->update(['image' => '']);
|
|
CmsBrand::query()->where('slug', 'rotpunkt')->update(['image' => 'marken/rotpunkt/kueche-01.webp']);
|
|
|
|
$slides = Livewire::test(HeroSlider::class)->get('slides');
|
|
|
|
expect(array_column($slides, 'label'))->toBe(['Rotpunkt Küchen']);
|
|
});
|
|
|
|
it('says so when no brand has an image yet', function () {
|
|
$this->seed(CmsBrandSeeder::class);
|
|
CmsBrand::query()->update(['image' => '']);
|
|
|
|
expect(heroSection($this->get('/entwurf')->assertSuccessful()->getContent()))->toContain('Noch keine Bilder hinterlegt');
|
|
});
|
|
|
|
it('prefers slides maintained in the cms and honours their active flag', function () {
|
|
$this->seed(CmsBrandSeeder::class);
|
|
$this->seed(BrandImageSeeder::class);
|
|
|
|
$slides = [
|
|
['image' => 'b2in/hero-room.jpg', 'label' => 'Raumkonzepte', 'caption' => 'Alles aus einer Hand'],
|
|
['image' => 'b2in/off.jpg', 'label' => 'Aus', 'active' => false],
|
|
['image' => '', 'label' => 'Ohne Bild'],
|
|
];
|
|
|
|
CmsContent::create([
|
|
'group' => 'home',
|
|
'key' => 'hero_slider',
|
|
'type' => 'json',
|
|
'value' => ['de' => ['title' => 'Test', 'slides' => $slides], 'en' => ['title' => 'Test', 'slides' => $slides]],
|
|
'order' => 0,
|
|
]);
|
|
|
|
$result = Livewire::test(HeroSlider::class)->get('slides');
|
|
|
|
expect($result)->toHaveCount(1)
|
|
->and($result[0]['label'])->toBe('Raumkonzepte')
|
|
->and($result[0]['image'])->toBe('b2in/hero-room.jpg');
|
|
});
|
|
|
|
it('draws the green flanks as inline svgs that scale with the frame', function () {
|
|
$hero = heroSection($this->get('/entwurf')->assertSuccessful()->getContent());
|
|
|
|
expect($hero)->toContain('data-hero-flank="right"')
|
|
->toContain('data-hero-flank="left"')
|
|
->toContain('preserveAspectRatio="xMaxYMid meet"')
|
|
->toContain('stop-color: hsl(var(--secondary-light))')
|
|
->toContain('class="hero-image-frame"');
|
|
});
|
|
|
|
it('clamps the slide interval to a calm range', function () {
|
|
CmsContent::create([
|
|
'group' => 'home',
|
|
'key' => 'hero_slider',
|
|
'type' => 'json',
|
|
'value' => ['de' => ['title' => 'Test', 'interval' => 1], 'en' => ['title' => 'Test', 'interval' => 1]],
|
|
'order' => 0,
|
|
]);
|
|
|
|
expect(Livewire::test(HeroSlider::class)->get('intervalMs'))->toBe(3000);
|
|
});
|
|
|
|
// ========================================
|
|
// Farb-Schalter
|
|
// ========================================
|
|
|
|
it('links the draft from the palette switch and back', function () {
|
|
config(['app.palette_switch' => true]);
|
|
|
|
$home = $this->get('/')->assertSuccessful()->getContent();
|
|
expect($home)->toContain('Entwurf')->toContain(route('home.draft'));
|
|
|
|
$draft = $this->get('/entwurf')->assertSuccessful()->getContent();
|
|
$switch = substr($draft, strpos($draft, 'data-palette-switch'));
|
|
|
|
expect($switch)->toContain('aria-current="page"')
|
|
->toContain('?palette=classic')
|
|
->toContain('?palette=modern')
|
|
->not->toContain('classList.add(\'palette-modern\')');
|
|
});
|
|
|
|
// ========================================
|
|
// Logos und Stylesheet
|
|
// ========================================
|
|
|
|
it('ships the gold logo copies with the new fir green and without the ocher', function () {
|
|
$positive = strtoupper(file_get_contents(public_path('img/logos/b2in-interior-logo-gold-positive.svg')));
|
|
$negative = strtoupper(file_get_contents(public_path('img/logos/b2in-interior-logo-gold-negative.svg')));
|
|
|
|
expect($positive)->toContain('#AA8341')->toContain('#14342C')->not->toContain('#D39E46')->not->toContain('#202C2B');
|
|
expect($negative)->toContain('#AA8341')->toContain('#F0EEEB')->not->toContain('#D39E46');
|
|
|
|
// Die bisherigen Logos bleiben unverändert.
|
|
expect(strtoupper(file_get_contents(public_path('img/logos/b2in-interior-logo-positive.svg'))))->toContain('#D39E46');
|
|
});
|
|
|
|
it('defines the greige palette on the body and keeps gold as a hairline', function () {
|
|
$css = file_get_contents(resource_path('css/web/theme-b2in.css'));
|
|
|
|
expect($css)->toContain('.world-interior.palette-greige {')
|
|
->toContain('--background: 45 14% 93%')
|
|
->toContain('--primary: 165 44% 14%')
|
|
->toContain('--secondary: 38 45% 46%')
|
|
->toContain('.world-interior.palette-greige .footer-logo-dark')
|
|
->toContain('.hero-draft .btn-hero')
|
|
// Goldkanten als Verlaufs-Border (padding-box/border-box), auch um das Bild; Button unten kräftiger.
|
|
->toContain(') border-box;')
|
|
->toContain('.hero-draft .hero-image-frame {')
|
|
->toContain('border-bottom-width: 3px')
|
|
->not->toContain('mask-composite');
|
|
});
|
|
|
|
// ========================================
|
|
// Projekttext (Briefing Punkt 7)
|
|
// ========================================
|
|
|
|
it('uses the new project wording in the lang files', function () {
|
|
$html = $this->get('/')->assertSuccessful()->getContent();
|
|
|
|
expect($html)->toContain('Mit einem zentralen deutschen Vertragspartner und einem eingespielten Herstellernetzwerk.')
|
|
->not->toContain('Mit deutscher Vertragssicherheit');
|
|
});
|
|
|
|
it('replaces the project wording in an existing cms row and leaves the rest alone', function () {
|
|
$cards = [
|
|
['title' => 'Zuhause', 'description' => 'Bleibt so.'],
|
|
['title' => 'Projekt', 'description' => 'Vollständige Einrichtung … Mit deutscher Vertragssicherheit und einem eingespielten Herstellernetzwerk.'],
|
|
];
|
|
CmsContent::create([
|
|
'group' => 'home',
|
|
'key' => 'audience_switch',
|
|
'type' => 'json',
|
|
'value' => ['de' => ['title' => 'Für wen?', 'cards' => $cards], 'en' => ['title' => 'For whom?', 'cards' => []]],
|
|
'order' => 0,
|
|
]);
|
|
|
|
$this->seed(AudienceSwitchProjectTextSeeder::class);
|
|
$this->seed(AudienceSwitchProjectTextSeeder::class);
|
|
|
|
$row = CmsContent::query()->where('group', 'home')->where('key', 'audience_switch')->firstOrFail();
|
|
$de = $row->getTranslation('value', 'de');
|
|
|
|
expect($de['title'])->toBe('Für wen?')
|
|
->and($de['cards'][0]['description'])->toBe('Bleibt so.')
|
|
->and($de['cards'][1]['description'])->toBe('Vollständige Einrichtung … Mit einem zentralen deutschen Vertragspartner und einem eingespielten Herstellernetzwerk.');
|
|
});
|