Skip to content

Commit

Permalink
Merge pull request #11 from kbond/reset-emails
Browse files Browse the repository at this point in the history
Reset collected emails
  • Loading branch information
kbond committed Oct 5, 2021
2 parents 2399e87 + fd083e0 commit f634112
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 196 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ class MyTest extends KernelTestCase // or WebTestCase
{
// ...some code that sends emails...
$this->mailer()->sentEmails(); // \Symfony\Component\Mime\Email[]
$this->mailer()->assertNoEmailSent();
$this->mailer()->assertSentEmailCount(5);
$this->mailer()->assertEmailSentTo('kevin@example.com', 'the subject');
Expand Down Expand Up @@ -66,12 +64,14 @@ class MyTest extends KernelTestCase // or WebTestCase
$this->assertSame('value', $email->getHeaders()->get('X-SOME-HEADER')->getBodyAsString());
});
$this->mailer()->sentTestEmails(); // TestEmail[]
// reset collected emails
$this->mailer()->reset();
}
}
```
**NOTE**: Emails are persisted between kernel reboots within each test.
**NOTE**: Emails are persisted between kernel reboots within each test. You can reset the
collected emails with `$this->mailer()->reset()`.
### Custom TestEmail
Expand Down
37 changes: 5 additions & 32 deletions src/Bridge/Zenstruck/Browser/MailerComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,17 @@

use Zenstruck\Browser\BrowserKitBrowser;
use Zenstruck\Browser\Component;
use Zenstruck\Mailer\Test\TestMailer;
use Zenstruck\Mailer\Test\SentEmailMixin;
use Zenstruck\Mailer\Test\SentEmails;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
final class MailerComponent extends Component
{
/**
* @see TestMailer::assertNoEmailSent()
*/
public function assertNoEmailSent(): self
{
$this->testMailer()->assertNoEmailSent();

return $this;
}

/**
* @see TestMailer::assertSentEmailCount()
*/
public function assertSentEmailCount(int $count): self
{
$this->testMailer()->assertSentEmailCount($count);

return $this;
}

/**
* @see TestMailer::assertEmailSentTo()
*/
public function assertEmailSentTo(string $expectedTo, $callback): self
{
$this->testMailer()->assertEmailSentTo($expectedTo, $callback);

return $this;
}
use SentEmailMixin;

private function testMailer(): TestMailer
public function sentEmails(): SentEmails
{
$browser = $this->browser();

Expand All @@ -53,6 +26,6 @@ private function testMailer(): TestMailer
throw new \RuntimeException('The profiler does not include the "mailer" collector. Is symfony/mailer installed?');
}

return new TestMailer($browser->profile()->getCollector('mailer')->getEvents());
return SentEmails::fromEvents($browser->profile()->getCollector('mailer')->getEvents());
}
}
38 changes: 6 additions & 32 deletions src/Bridge/Zenstruck/Browser/MailerExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,18 @@

namespace Zenstruck\Mailer\Test\Bridge\Zenstruck\Browser;

use Zenstruck\Mailer\Test\SentEmailMixin;
use Zenstruck\Mailer\Test\SentEmails;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
trait MailerExtension
{
/**
* @see TestMailer::assertNoEmailSent()
*
* @return static
*/
public function assertNoEmailSent(): self
{
return $this->use(function(MailerComponent $component) {
$component->assertNoEmailSent();
});
}

/**
* @see TestMailer::assertSentEmailCount()
*
* @return static
*/
public function assertSentEmailCount(int $count): self
{
return $this->use(function(MailerComponent $component) use ($count) {
$component->assertSentEmailCount($count);
});
}
use SentEmailMixin;

/**
* @see TestMailer::assertEmailSentTo()
*
* @return static
*/
public function assertEmailSentTo(string $expectedTo, $callback): self
public function sentEmails(): SentEmails
{
return $this->use(function(MailerComponent $component) use ($expectedTo, $callback) {
$component->assertEmailSentTo($expectedTo, $callback);
});
return (new MailerComponent($this))->sentEmails();
}
}
14 changes: 7 additions & 7 deletions src/InteractsWithMailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ trait InteractsWithMailer
* @internal
* @before
*/
final protected static function _startTestMailerCollection(): void
final protected static function _startTestMailer(): void
{
MessageEventCollector::start();
TestMailer::start();
}

/**
* @internal
* @after
*/
final protected static function _resetTestMailerCollection(): void
final protected static function _stopTestMailer(): void
{
MessageEventCollector::reset();
TestMailer::stop();
}

final protected function mailer(): TestMailer
Expand All @@ -37,10 +37,10 @@ final protected function mailer(): TestMailer
throw new \LogicException('The kernel must be booted before accessing the mailer.');
}

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));
if (!self::$container->has('zenstruck_mailer_test.mailer')) {
throw new \LogicException(\sprintf('Cannot access test mailer - is %s enabled in your test environment?', ZenstruckMailerTestBundle::class));
}

return self::$container->get('zenstruck_mailer_test.event_collector')->mailer();
return self::$container->get('zenstruck_mailer_test.mailer');
}
}
50 changes: 0 additions & 50 deletions src/MessageEventCollector.php

This file was deleted.

72 changes: 72 additions & 0 deletions src/SentEmailMixin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace Zenstruck\Mailer\Test;

use Symfony\Component\Mime\Address;
use Zenstruck\Assert;
use Zenstruck\Callback;
use Zenstruck\Callback\Parameter;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
trait SentEmailMixin
{
/**
* @return static
*/
public function assertNoEmailSent(): self
{
$this->sentEmails()->assertNone();

return $this;
}

/**
* @return static
*/
public function assertSentEmailCount(int $expected): self
{
$this->sentEmails()->assertCount($expected);

return $this;
}

/**
* @param callable|string $callback Takes an instance of the found Email as TestEmail - if string, assume subject
*
* @return static
*/
public function assertEmailSentTo(string $expectedTo, $callback): self
{
$emails = $this->sentEmails();

if (0 === \count($emails)) {
Assert::fail('No emails have been sent.');
}

if (!\is_callable($callback)) {
$callback = static fn(TestEmail $message) => $message->assertSubject($callback);
}

$foundToAddresses = [];

foreach ($emails as $email) {
$toAddresses = \array_map(static fn(Address $address) => $address->getAddress(), $email->getTo());
$foundToAddresses[] = $toAddresses;

if (\in_array($expectedTo, $toAddresses, true)) {
// address matches
Callback::createFor($callback)->invoke(
Parameter::typed(TestEmail::class, Parameter::factory(fn(string $class) => new $class($email)))
);

return $this;
}
}

Assert::fail(\sprintf('Email sent, but "%s" is not among to-addresses: %s', $expectedTo, \implode(', ', \array_merge(...$foundToAddresses))));
}

abstract public function sentEmails(): SentEmails;
}
85 changes: 85 additions & 0 deletions src/SentEmails.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace Zenstruck\Mailer\Test;

use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mailer\Event\MessageEvents;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\RawMessage;
use Zenstruck\Assert;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
final class SentEmails implements \IteratorAggregate, \Countable
{
/** @var Email[] */
private array $emails;

public function __construct(Email ...$emails)
{
$this->emails = $emails;
}

public static function fromEvents(MessageEvents $events): self
{
$usingQueue = false;
$events = $events->getEvents();

foreach ($events as $event) {
if ($event->isQueued()) {
$usingQueue = true;

break;
}
}

if ($usingQueue) {
// if using queue, remove non queued messages to avoid duplicates
$events = \array_filter($events, static fn(MessageEvent $event) => $event->isQueued());
}

// convert events to messages
$messages = \array_map(static fn(MessageEvent $event) => $event->getMessage(), $events);

// remove non Email messages
$messages = \array_filter($messages, static fn(RawMessage $message) => $message instanceof Email);

return new self(...$messages);
}

/**
* @return Email[]
*/
public function all(): array
{
return $this->emails;
}

public function assertCount(int $expected): self
{
Assert::that($this->emails)
->hasCount($expected, 'Expected {expected} emails to be sent, but {actual} emails were sent.')
;

return $this;
}

public function assertNone(): self
{
return $this->assertCount(0);
}

/**
* @return \Traversable|Email[]
*/
public function getIterator(): \Traversable
{
return new \ArrayIterator($this->emails);
}

public function count(): int
{
return \count($this->emails);
}
}
Loading

0 comments on commit f634112

Please sign in to comment.