diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..402bd2f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.yml] +indent_size = 2 diff --git a/README.md b/README.md index f1edc33..2d90911 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,12 @@ an alternative to the FrameworkBundle's `MailerAssertionsTrait`. ## Installation -```bash -composer require --dev zenstruck/mailer-test -``` +1. Install the library: + ```bash + composer require --dev zenstruck/mailer-test + ``` +2. If not added automatically by symfony/flex, enable `ZenstruckMailerTestBundle` in your + `test` environment ## Usage @@ -62,12 +65,14 @@ class MyTest extends KernelTestCase // or WebTestCase // Any \Symfony\Component\Mime\Email methods can be used $this->assertSame('value', $email->getHeaders()->get('X-SOME-HEADER')->getBodyAsString()); }); - + $this->mailer()->sentTestEmails(); // TestEmail[] } } ``` +**NOTE**: Emails are persisted between kernel reboots within each test. + ### Custom TestEmail The `TestEmail` class shown above is a decorator for `\Symfony\Component\Mime\Email` @@ -143,7 +148,7 @@ $browser ->use(function(MailerComponent $component) { $component->assertNoEmailSent(); }) - + ->withProfiling() // enable the profiler for the next request ->visit('/page/that/sends/email') ->use(function(MailerComponent $component) { @@ -188,7 +193,7 @@ $browser ->withProfiling() // enable the profiler for the next request ->visit('/page/that/does/not/send/email') ->assertNoEmailSent() - + ->withProfiling() // enable the profiler for the next request ->visit('/page/that/sends/email') ->assertSentEmailCount(1) diff --git a/src/InteractsWithMailer.php b/src/InteractsWithMailer.php index c056250..d743b8b 100644 --- a/src/InteractsWithMailer.php +++ b/src/InteractsWithMailer.php @@ -9,6 +9,24 @@ */ trait InteractsWithMailer { + /** + * @internal + * @before + */ + final protected static function _startTestMailerCollection(): void + { + MessageEventCollector::start(); + } + + /** + * @internal + * @after + */ + final protected static function _resetTestMailerCollection(): void + { + MessageEventCollector::reset(); + } + final protected function mailer(): TestMailer { if (!$this instanceof KernelTestCase) { @@ -19,14 +37,10 @@ final protected function mailer(): TestMailer throw new \LogicException('The kernel must be booted before accessing the mailer.'); } - if (self::$container->has('mailer.message_logger_listener')) { - return new TestMailer(self::$container->get('mailer.message_logger_listener')->getEvents()); - } - - if (self::$container->has('mailer.logger_message_listener')) { - return new TestMailer(self::$container->get('mailer.logger_message_listener')->getEvents()); + if (!self::$container->has('zenstruck_mailer_test.event_collector')) { + throw new \LogicException(\sprintf('Cannot access collected emails - is %s enabled in your test environment?', ZenstruckMailerTestBundle::class)); } - throw new \LogicException('Mailer and/or profiling not enabled.'); + return self::$container->get('zenstruck_mailer_test.event_collector')->mailer(); } } diff --git a/src/MessageEventCollector.php b/src/MessageEventCollector.php new file mode 100644 index 0000000..9012a57 --- /dev/null +++ b/src/MessageEventCollector.php @@ -0,0 +1,50 @@ + + * + * @internal + */ +final class MessageEventCollector implements EventSubscriberInterface +{ + private static ?MessageEvents $events = null; + + public function onMessage(MessageEvent $event): void + { + if (self::$events) { + self::$events->add($event); + } + } + + public function mailer(): TestMailer + { + if (!self::$events) { + throw new \LogicException('Cannot access mailer as email collection has not yet been started.'); + } + + return new TestMailer(self::$events); + } + + public static function start(): void + { + self::$events = new MessageEvents(); + } + + public static function reset(): void + { + self::$events = null; + } + + public static function getSubscribedEvents(): array + { + return [ + MessageEvent::class => ['onMessage', -255], + ]; + } +} diff --git a/src/ZenstruckMailerTestBundle.php b/src/ZenstruckMailerTestBundle.php new file mode 100644 index 0000000..2be02b1 --- /dev/null +++ b/src/ZenstruckMailerTestBundle.php @@ -0,0 +1,25 @@ + + */ +final class ZenstruckMailerTestBundle extends Bundle +{ + public function build(ContainerBuilder $container): void + { + $container->register('zenstruck_mailer_test.event_collector', MessageEventCollector::class) + ->addTag('kernel.event_subscriber') + ; + } + + public function getContainerExtension(): ?ExtensionInterface + { + return null; + } +} diff --git a/tests/Fixture/Kernel.php b/tests/Fixture/Kernel.php index 687b1bd..57f09c4 100644 --- a/tests/Fixture/Kernel.php +++ b/tests/Fixture/Kernel.php @@ -9,6 +9,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\RouteCollectionBuilder; +use Zenstruck\Mailer\Test\ZenstruckMailerTestBundle; /** * @author Kevin Bond @@ -32,6 +33,10 @@ public function sendEmail(): Response public function registerBundles(): iterable { yield new FrameworkBundle(); + + if ('no_bundle' !== $this->environment) { + yield new ZenstruckMailerTestBundle(); + } } protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader): void diff --git a/tests/Fixture/config/no_bundle.yaml b/tests/Fixture/config/no_bundle.yaml new file mode 100644 index 0000000..5d1ac0b --- /dev/null +++ b/tests/Fixture/config/no_bundle.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: test.yaml } diff --git a/tests/Fixture/config/no_mailer.yaml b/tests/Fixture/config/no_mailer.yaml deleted file mode 100644 index 12d6e65..0000000 --- a/tests/Fixture/config/no_mailer.yaml +++ /dev/null @@ -1,8 +0,0 @@ -framework: - secret: S3CRET - router: { utf8: true } - test: true - profiler: - collect: false - messenger: false - mailer: false diff --git a/tests/InteractsWithMailerTest.php b/tests/InteractsWithMailerTest.php index ec20737..c7a28d3 100644 --- a/tests/InteractsWithMailerTest.php +++ b/tests/InteractsWithMailerTest.php @@ -8,7 +8,7 @@ use Zenstruck\Mailer\Test\TestEmail; use Zenstruck\Mailer\Test\Tests\Fixture\CustomTestEmail; use Zenstruck\Mailer\Test\Tests\Fixture\Email1; -use Zenstruck\Mailer\Test\Tests\Fixture\Kernel; +use Zenstruck\Mailer\Test\ZenstruckMailerTestBundle; /** * @author Kevin Bond @@ -160,48 +160,46 @@ public function kernel_must_be_booted(): void /** * @test */ - public function mailer_must_be_enabled(): void + public function profiler_does_not_need_to_enabled(): void { - self::bootKernel(['environment' => 'no_mailer']); + self::bootKernel(['environment' => 'no_profiler']); - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Mailer and/or profiling not enabled'); + self::$container->get('mailer')->send(new Email1()); - $this->mailer(); + $this->mailer()->assertSentEmailCount(1); } /** * @test + * @dataProvider environmentProvider */ - public function profiler_must_be_enabled_in_symfony_5_2(): void + public function emails_are_persisted_between_reboots(string $environment): void { - if ('52' !== Kernel::MAJOR_VERSION.Kernel::MINOR_VERSION) { - // profile needs to be enabled in 5.2 only - $this->markTestSkipped(); - } + self::bootKernel(['environment' => $environment]); - self::bootKernel(['environment' => 'no_profiler']); + $this->mailer()->assertNoEmailSent(); - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Mailer and/or profiling not enabled'); + self::$container->get('mailer')->send(new Email1()); - $this->mailer(); + $this->mailer()->assertSentEmailCount(1); + + self::ensureKernelShutdown(); + + self::bootKernel(['environment' => $environment]); + + $this->mailer()->assertSentEmailCount(1); } /** * @test */ - public function profiler_does_not_need_to_enabled(): void + public function bundle_must_be_enabled(): void { - if ('52' === Kernel::MAJOR_VERSION.Kernel::MINOR_VERSION) { - // profile does not need to be enabled in versions other than 52 - $this->markTestSkipped(); - } + self::bootKernel(['environment' => 'no_bundle']); - self::bootKernel(['environment' => 'no_profiler']); - - self::$container->get('mailer')->send(new Email1()); + $this->expectException(\LogicException::class); + $this->expectExceptionMessage(\sprintf('Cannot access collected emails - is %s enabled in your test environment?', ZenstruckMailerTestBundle::class)); - $this->mailer()->assertSentEmailCount(1); + $this->mailer(); } } diff --git a/tests/NonInteractsWithMailerTest.php b/tests/NonInteractsWithMailerTest.php new file mode 100644 index 0000000..1a4c75d --- /dev/null +++ b/tests/NonInteractsWithMailerTest.php @@ -0,0 +1,27 @@ + + */ +final class NonInteractsWithMailerTest extends KernelTestCase +{ + /** + * @test + */ + public function ensure_emails_are_not_collected(): void + { + self::bootKernel(); + + self::$container->get('mailer')->send(new Email1()); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot access mailer as email collection has not yet been started.'); + + self::$container->get('zenstruck_mailer_test.event_collector')->mailer(); + } +}