forceRootUrl('https://'.config('domains.domain_portal')); }); it('requires authentication', function () { $this->get(route('cms.brands.index'))->assertRedirect('/login'); }); it('renders for authenticated users', function () { $this->actingAs(User::factory()->create()) ->get(route('cms.brands.index')) ->assertSuccessful() ->assertSee('Kernmarken'); }); it('creates a brand', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'neue-marke') ->set('brandName', 'Neue Marke') ->set('tagline', 'Ein Claim.') ->set('teaser', 'Kurztext für die Kachel.') ->call('save') ->assertHasNoErrors(); $brand = CmsBrand::where('slug', 'neue-marke')->firstOrFail(); expect($brand->getTranslation('name', 'de'))->toBe('Neue Marke'); expect($brand->getTranslation('tagline', 'de'))->toBe('Ein Claim.'); expect($brand->is_published)->toBeTrue(); }); it('rejects a slug that is already taken', function () { CmsBrand::factory()->create(['slug' => 'cabinet']); $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'cabinet') ->set('brandName', 'Zweite CABINET') ->call('save'); expect(CmsBrand::where('slug', 'cabinet')->count())->toBe(1); }); it('rejects a slug with invalid characters', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'Nicht Erlaubt!') ->set('brandName', 'Test') ->call('save') ->assertHasErrors(['slug']); }); it('requires a name', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'ohne-namen') ->set('brandName', '') ->call('save') ->assertHasErrors(['brandName']); }); it('edits an existing brand', function () { $brand = CmsBrand::factory()->create(['slug' => 'cabinet', 'name' => ['de' => 'CABINET']]); $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openEdit', $brand->id) ->assertSet('slug', 'cabinet') ->set('tagline', 'Neuer Claim.') ->call('save') ->assertHasNoErrors(); expect($brand->fresh()->getTranslation('tagline', 'de'))->toBe('Neuer Claim.'); }); it('keeps both languages side by side', function () { $brand = CmsBrand::factory()->create(['slug' => 'cabinet', 'name' => ['de' => 'CABINET', 'en' => 'CABINET']]); $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openEdit', $brand->id) ->set('tagline', 'Deutscher Claim') ->call('save') ->call('openEdit', $brand->id) ->call('switchLocale', 'en') ->set('tagline', 'English claim') ->call('save'); expect($brand->fresh()->getTranslation('tagline', 'de'))->toBe('Deutscher Claim'); expect($brand->fresh()->getTranslation('tagline', 'en'))->toBe('English claim'); }); it('drops empty service rows on save', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'mit-leistungen') ->set('brandName', 'Mit Leistungen') ->set('services', [ ['title' => 'Aufmaß', 'description' => 'Vor Ort.'], ['title' => '', 'description' => ''], ]) ->call('save'); expect(CmsBrand::where('slug', 'mit-leistungen')->firstOrFail()->services)->toHaveCount(1); }); it('toggles the published state', function () { $brand = CmsBrand::factory()->create(['is_published' => true]); $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index')->call('togglePublished', $brand->id); expect($brand->fresh()->is_published)->toBeFalse(); }); it('deletes a brand', function () { $brand = CmsBrand::factory()->create(); $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index')->call('deleteBrand', $brand->id); expect(CmsBrand::find($brand->id))->toBeNull(); }); it('saves the overview text and the highlights', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'mit-merkmalen') ->set('brandName', 'Mit Merkmalen') ->set('summary', "Erster Absatz.\n\nZweiter Absatz.") ->set('highlights', ['Erstes Merkmal', '', 'Zweites Merkmal']) ->call('save') ->assertHasNoErrors(); $brand = CmsBrand::where('slug', 'mit-merkmalen')->firstOrFail(); expect($brand->getTranslation('summary', 'de'))->toBe("Erster Absatz.\n\nZweiter Absatz."); expect($brand->highlights)->toBe(['Erstes Merkmal', 'Zweites Merkmal']); }); it('shows a brand added in the admin on the overview page', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'ganz-neue-marke') ->set('brandName', 'Ganz neue Marke') ->set('tagline', 'Frisch dabei.') ->set('summary', 'Beschreibung für die Übersicht.') ->set('highlights', ['Ein Merkmal']) ->call('save'); $this->get('/marken') ->assertSuccessful() ->assertSee('Ganz neue Marke') ->assertSee('Beschreibung für die Übersicht.', false) ->assertSee('Ein Merkmal') ->assertSee('/marken/ganz-neue-marke'); }); // ======================================== // Galerie // ======================================== it('adds gallery entries with the category that was chosen', function () { $this->actingAs(User::factory()->create()); $component = Volt::test('admin.cms.brands-index') ->call('openCreate') ->call('addGalleryImage', 'project') ->call('addGalleryImage', 'inspiration'); expect($component->get('gallery')[0]['category'])->toBe('project') ->and($component->get('gallery')[1]['category'])->toBe('inspiration'); }); it('saves the gallery with category and caption', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'mit-galerie') ->set('brandName', 'Mit Galerie') ->set('gallery', [ ['image' => 'projekt.webp', 'category' => 'project', 'caption' => 'Ankleide in Bielefeld'], ['image' => 'entwurf.webp', 'category' => 'inspiration', 'caption' => ''], ]) ->call('save') ->assertHasNoErrors(); $gallery = CmsBrand::where('slug', 'mit-galerie')->firstOrFail()->gallery; expect($gallery)->toBe([ ['image' => 'projekt.webp', 'category' => 'project', 'caption' => 'Ankleide in Bielefeld'], ['image' => 'entwurf.webp', 'category' => 'inspiration', 'caption' => ''], ]); }); it('does not let an unknown category slip through as a completed project', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'geschummelt') ->set('brandName', 'Geschummelt') ->set('gallery', [['image' => 'a.webp', 'category' => 'referenz']]) ->call('save'); expect(CmsBrand::where('slug', 'geschummelt')->firstOrFail()->gallery[0]['category']) ->toBe(CmsBrand::GALLERY_INSPIRATION); }); it('removes a gallery entry', function () { $brand = CmsBrand::factory()->withGallery(projects: 2, inspirations: 1)->create(['slug' => 'mit-bildern']); $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openEdit', $brand->id) ->call('removeGalleryImage', 0) ->call('save'); expect($brand->fresh()->gallery)->toHaveCount(2); }); // ======================================== // Abschnitte der Markenseite // ======================================== it('saves sections with a deepening', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'mit-abschnitten') ->set('brandName', 'Mit Abschnitten') ->call('addSection') ->set('sections.0.title', 'Aus Ostwestfalen') ->set('sections.0.paragraphs', "Erster Absatz.\n\nZweiter Absatz.") ->set('sections.0.claim', 'Eine Schlusszeile.') ->set('sections.0.detail_title', 'Vertiefung') ->set('sections.0.detail_paragraphs', 'Mehr dazu.') ->call('save') ->assertHasNoErrors(); $sections = CmsBrand::where('slug', 'mit-abschnitten')->firstOrFail()->sections; expect($sections)->toHaveCount(1) ->and($sections[0]['paragraphs'])->toBe(['Erster Absatz.', 'Zweiter Absatz.']) ->and($sections[0]['claim'])->toBe('Eine Schlusszeile.') ->and($sections[0]['detail']['title'])->toBe('Vertiefung') ->and($sections[0]['detail']['paragraphs'])->toBe(['Mehr dazu.']); }); it('reorders sections', function () { $brand = CmsBrand::factory()->create([ 'slug' => 'sortieren', 'sections' => [ ['title' => 'Erster', 'paragraphs' => ['A']], ['title' => 'Zweiter', 'paragraphs' => ['B']], ], ]); $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openEdit', $brand->id) ->call('moveSection', 1, -1) ->call('save'); expect($brand->fresh()->sections[0]['title'])->toBe('Zweiter'); }); it('does not move a section beyond the ends', function () { $brand = CmsBrand::factory()->create([ 'slug' => 'raender', 'sections' => [['title' => 'Einziger', 'paragraphs' => ['A']]], ]); $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openEdit', $brand->id) ->call('moveSection', 0, -1) ->call('moveSection', 0, 1) ->call('save'); expect($brand->fresh()->sections)->toHaveCount(1); }); it('saves the widget label and the closing block', function () { $this->actingAs(User::factory()->create()); Volt::test('admin.cms.brands-index') ->call('openCreate') ->set('slug', 'mit-cta') ->set('brandName', 'Mit CTA') ->set('widgetLabel', 'Persönliche Küchenberatung vereinbaren') ->set('ctaTitle', 'Ihre Küche. Persönlich geplant.') ->set('ctaText', 'Jeder Raum ist anders.') ->call('save') ->assertHasNoErrors(); $brand = CmsBrand::where('slug', 'mit-cta')->firstOrFail(); expect($brand->getTranslation('widget_label', 'de'))->toBe('Persönliche Küchenberatung vereinbaren') ->and($brand->getTranslation('cta_title', 'de'))->toBe('Ihre Küche. Persönlich geplant.') ->and($brand->getTranslation('cta_text', 'de'))->toBe('Jeder Raum ist anders.'); });