Der Editor packt eingefügten Text in <p>; Felder wie die FAQ-Antwort gibt die Website als Text aus, die Tags waren sichtbar. Beim Speichern entfernt unwrapParagraphs() die Absätze (ein Absatz ohne <p>, mehrere mit <br><br>, Blockinhalte unverändert). cms_inline() gibt die Felder als bereinigtes Inline-HTML aus – auch schon gespeicherte Texte mit <p> erscheinen sauber, Fett und Hervorhebung werden dargestellt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
3.1 KiB
PHP
69 lines
3.1 KiB
PHP
<?php
|
|
|
|
use App\Models\User;
|
|
use App\Services\CmsFluxEditorHtmlTransformer;
|
|
use Database\Seeders\CmsContentSeeder;
|
|
use FluxCms\Core\Models\CmsContent;
|
|
use Livewire\Volt\Volt;
|
|
|
|
/**
|
|
* Der Rich-Text-Editor packt eingefügten Text in <p>; auf der Website erschienen die Tags als
|
|
* Text, z. B. in der FAQ (12.10.2026).
|
|
*/
|
|
it('stores pasted text from the editor without paragraph tags', function (string $editor, string $stored) {
|
|
expect(CmsFluxEditorHtmlTransformer::unwrapParagraphs($editor))->toBe($stored);
|
|
})->with([
|
|
'ein Absatz' => ['<p>Eingefügter Text</p>', 'Eingefügter Text'],
|
|
'Sonderzeichen' => ['<p>Planung & Montage</p>', 'Planung & Montage'],
|
|
'fett' => ['<p>Mit <strong>fett</strong></p>', 'Mit <strong>fett</strong>'],
|
|
'zwei Absätze' => ['<p>Eins</p><p>Zwei</p>', 'Eins<br><br>Zwei'],
|
|
'leere Absätze' => ['<p>Text</p><p></p><p><br></p>', 'Text'],
|
|
'Blockinhalt bleibt' => ['<h2>Titel</h2><p>Text</p>', '<h2>Titel</h2><p>Text</p>'],
|
|
'schon Klartext' => ['Ohne Tags', 'Ohne Tags'],
|
|
]);
|
|
|
|
it('renders cms text inline: plain text escaped, editor html without paragraphs, nothing unsafe', function (string $value, string $html) {
|
|
expect((string) cms_inline($value))->toBe($html);
|
|
})->with([
|
|
['Planung & Montage', 'Planung & Montage'],
|
|
['<p>Schon gespeichert</p>', 'Schon gespeichert'],
|
|
['<p>Mit <mark>Hervorhebung</mark></p>', 'Mit <span class="text-secondary">Hervorhebung</span>'],
|
|
['<p>a</p><p>b</p>', 'a<br><br>b'],
|
|
['<a href="javascript:alert(1)" onclick="x()">Link</a>', '<a href="#">Link</a>'],
|
|
['<p><script></p>', ''],
|
|
]);
|
|
|
|
it('saves a pasted faq answer without p tags and shows it cleanly on the page', function () {
|
|
$this->actingAs(User::factory()->create());
|
|
$this->seed(CmsContentSeeder::class);
|
|
$row = CmsContent::query()->where('group', 'faq')->where('key', 'faq')->first();
|
|
|
|
$component = Volt::test('admin.cms.content-index')
|
|
->call('startFieldEdit', $row->id, 'questions');
|
|
|
|
$component->set('jsonItems.0.answer', '<p>Neue Antwort aus der Zwischenablage</p>')
|
|
->call('saveJsonModal');
|
|
|
|
expect($row->refresh()->getTranslation('value', 'de')['questions'][0]['answer'])->toBe('Neue Antwort aus der Zwischenablage');
|
|
|
|
$page = $this->get('/faq')->assertSuccessful()->getContent();
|
|
|
|
expect(substr($page, strpos($page, 'id="faq"')))->toContain('Neue Antwort aus der Zwischenablage')
|
|
->not->toContain('<p>');
|
|
});
|
|
|
|
it('shows answers that were already stored with p tags without the tags', function () {
|
|
$this->seed(CmsContentSeeder::class);
|
|
$row = CmsContent::query()->where('group', 'faq')->where('key', 'faq')->first();
|
|
$value = $row->getTranslation('value', 'de');
|
|
$value['questions'][0]['answer'] = '<p>Bereits gespeichert mit Absatz</p>';
|
|
$row->setTranslation('value', 'de', $value)->save();
|
|
app(FluxCms\Core\Services\CmsContentService::class)->clearCache('faq');
|
|
|
|
$page = $this->get('/faq')->assertSuccessful()->getContent();
|
|
$section = substr($page, strpos($page, 'id="faq"'));
|
|
|
|
expect($section)->toContain('Bereits gespeichert mit Absatz')
|
|
->not->toContain('<p>')
|
|
->not->toContain('<p>Bereits');
|
|
});
|