get(EmailTextCatalog::class); $definition = $catalog->get($key); $placeholders = []; foreach ($definition->getPlaceholderNames() as $name) { $placeholders[$name] = '['.$name.']'; } // Renders the delivered wording: the test environment has no database, and the // defaults are what a mail falls back to anyway. $repository = $this->createMock(EmailTextRepository::class); $repository->method('findByKey')->willReturn(null); $text = (new EmailTextRenderer($catalog, $repository, $container->get(MailBodyRenderer::class))) ->render($key, $placeholders) ; $email = $container->get(Mailer::class)->create(['text' => $text], [ 'from' => 'test@example.com', 'to' => 'teamer@example.com', 'subject' => $text->subject, 'template' => 'email/generic.html.twig', 'subject_parameters' => [], 'attachments' => [], 'transport' => null, 'bus_transport' => null, ]); $html = $email->getHtmlBody(); $this->assertNotEmpty($html); $this->assertStringContainsString($text->headline, $html); // The layout closes every mail with the same button, pointing at the site root // rather than a deep link so that a client's link preview cannot trip the // unauthorized-access logging. $this->assertStringContainsString('class="button">Zum Portal', $html); $this->assertStringNotContainsString('/teamer/disposition/detail', $html); // Every placeholder the mail declares has to reach the rendered body, or the // wording lost a detail somewhere between catalogue and template. foreach ($definition->getPlaceholderNames() as $name) { $this->assertStringContainsString( '['.$name.']', $html.$email->getSubject(), sprintf('Placeholder "%s" does not show up in the rendered mail', $name) ); } } public static function provideKeys(): iterable { foreach (EmailTextKey::cases() as $key) { yield $key->value => [$key]; } } }