feat: markdown in email body for mailing and transactional

This commit is contained in:
Björn Fromme
2026-08-20 14:00:53 +02:00
parent f0e850978b
commit 5912a75c81
23 changed files with 1251 additions and 160 deletions
+55 -10
View File
@@ -7,6 +7,7 @@ namespace App\Tests\Email;
use App\Config\EmailTextCatalog;
use App\Config\EmailTextKey;
use App\Email\EmailTextRenderer;
use App\Email\MailBodyRenderer;
use App\Entity\EmailText;
use App\Repository\EmailTextRepository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
@@ -41,7 +42,11 @@ class EmailTextRendererTest extends KernelTestCase
$this->assertStringContainsString('Skireise', $rendered->bodyHtml);
}
public function testMarkupTypedByAnAdminIsEscaped(): void
/**
* Admins write Markdown, so markup they type is dropped rather than shown back to them
* as entities - a mail full of <script> would be nobody's intention either.
*/
public function testMarkupTypedByAnAdminIsStripped(): void
{
$rendered = $this->render(
$this->emailText(body: 'Hallo <script>alert(1)</script>'),
@@ -49,7 +54,7 @@ class EmailTextRendererTest extends KernelTestCase
);
$this->assertStringNotContainsString('<script>', $rendered->bodyHtml);
$this->assertStringContainsString('&lt;script&gt;', $rendered->bodyHtml);
$this->assertStringNotContainsString('&lt;script&gt;', $rendered->bodyHtml);
}
/**
@@ -66,16 +71,53 @@ class EmailTextRendererTest extends KernelTestCase
$this->assertStringContainsString('&lt;img', $rendered->bodyHtml);
}
public function testAsterisksMakeTextBold(): void
public function testDoubleAsterisksMakeTextBold(): void
{
$rendered = $this->render(
$this->emailText(body: 'Das ist *wichtig* und das nicht'),
$this->emailText(body: 'Das ist **wichtig** und das nicht'),
['destination' => 'Skireise']
);
$this->assertSame('<p>Das ist <strong>wichtig</strong> und das nicht</p>', $rendered->bodyHtml);
}
public function testSingleAsterisksMakeTextItalic(): void
{
$rendered = $this->render(
$this->emailText(body: 'Das ist *anders* gemeint'),
['destination' => 'Skireise']
);
$this->assertSame('<p>Das ist <em>anders</em> gemeint</p>', $rendered->bodyHtml);
}
public function testDashesAndNumbersBecomeLists(): void
{
$rendered = $this->render(
$this->emailText(body: "- Ausweis\n- Bankverbindung"),
['destination' => 'Skireise']
);
$this->assertSame(
"<ul>\n<li>Ausweis</li>\n<li>Bankverbindung</li>\n</ul>",
$rendered->bodyHtml
);
}
/**
* The mail sets its own h1 above the body and styles nothing below h3, so an admin's
* heading is folded into what is left.
*/
public function testHeadingsAreFoldedIntoTheLevelsTheMailStyles(): void
{
$rendered = $this->render(
$this->emailText(body: "# Ganz gross\n\n#### Ganz klein"),
['destination' => 'Skireise']
);
$this->assertSame("<h2>Ganz gross</h2>\n<h3>Ganz klein</h3>", $rendered->bodyHtml);
}
public function testAnEscapedAsteriskStaysLiteral(): void
{
$rendered = $this->render(
@@ -94,11 +136,11 @@ class EmailTextRendererTest extends KernelTestCase
{
$rendered = $this->render(
$this->emailText(body: 'Einsatz {destination}'),
['destination' => '*nicht fett*']
['destination' => '**nicht fett**']
);
$this->assertStringNotContainsString('<strong>', $rendered->bodyHtml);
$this->assertStringContainsString('*nicht fett*', $rendered->bodyHtml);
$this->assertStringContainsString('**nicht fett**', $rendered->bodyHtml);
}
public function testAnEmptyPlaceholderFallsBackToADash(): void
@@ -196,14 +238,15 @@ class EmailTextRendererTest extends KernelTestCase
$renderer = new EmailTextRenderer(
self::getContainer()->get(EmailTextCatalog::class),
$repository
$repository,
self::getContainer()->get(MailBodyRenderer::class)
);
$rendered = $renderer->renderValues(
EmailTextKey::APPLICATION_REJECTED,
'Entwurf {destination}',
'Überschrift',
'Entwurfstext *fett*',
'Entwurfstext **fett**',
['destination' => 'Skireise']
);
@@ -221,7 +264,8 @@ class EmailTextRendererTest extends KernelTestCase
$renderer = new EmailTextRenderer(
self::getContainer()->get(EmailTextCatalog::class),
$this->createMock(EmailTextRepository::class)
$this->createMock(EmailTextRepository::class),
self::getContainer()->get(MailBodyRenderer::class)
);
$rendered = $renderer->renderValues(
@@ -252,7 +296,8 @@ class EmailTextRendererTest extends KernelTestCase
$renderer = new EmailTextRenderer(
self::getContainer()->get(EmailTextCatalog::class),
$repository
$repository,
self::getContainer()->get(MailBodyRenderer::class)
);
return $renderer->render(EmailTextKey::APPLICATION_REJECTED, $placeholders);