From 20d263562a829d15100750047bec309d74650e5c Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 7 Oct 2021 15:47:09 -0400 Subject: [PATCH] [feature] add SentEmails collection methods/filters --- composer.json | 2 + src/SentEmailMixin.php | 12 ++---- src/SentEmails.php | 83 ++++++++++++++++++++++++++++++++++++++++++ src/TestEmail.php | 16 ++++++++ 4 files changed, 104 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 93db5e2..01c505f 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "php": ">=7.4", "symfony/framework-bundle": "^4.4|^5.0", "symfony/mailer": "^4.4|^5.0", + "symfony/polyfill-php80": "^1.23.1", "zenstruck/assert": "^1.0", "zenstruck/callback": "^1.1" }, @@ -22,6 +23,7 @@ "phpunit/phpunit": "^9.5.0", "symfony/messenger": "^4.4|^5.0", "symfony/phpunit-bridge": "^5.2", + "symfony/var-dumper": "^4.4|^5.0", "symfony/yaml": "^4.4|^5.0", "zenstruck/browser": "^1.0@dev" }, diff --git a/src/SentEmailMixin.php b/src/SentEmailMixin.php index 8d6348b..d293db3 100644 --- a/src/SentEmailMixin.php +++ b/src/SentEmailMixin.php @@ -3,10 +3,7 @@ namespace Zenstruck\Mailer\Test; use Symfony\Component\Mime\Address; -use Symfony\Component\Mime\Email; use Zenstruck\Assert; -use Zenstruck\Callback; -use Zenstruck\Callback\Parameter; /** * @author Kevin Bond @@ -34,7 +31,8 @@ public function assertSentEmailCount(int $expected): self } /** - * @param callable|string $callback Takes an instance of the found Email as TestEmail - if string, assume subject + * @param callable|string $callback callable: {@see TestEmail::call()} + * string: subject * * @return static */ @@ -52,11 +50,7 @@ public function assertEmailSentTo(string $expectedTo, $callback): self if (\in_array($expectedTo, $toAddresses, true)) { // address matches - Callback::createFor($callback)->invoke(Parameter::union( - Parameter::untyped($email), - Parameter::typed(Email::class, $email->inner()), - Parameter::typed(TestEmail::class, Parameter::factory(fn(string $class) => $email->as($class))) - )); + $email->call($callback); return $this; } diff --git a/src/SentEmails.php b/src/SentEmails.php index 01a6f00..fd05bf9 100644 --- a/src/SentEmails.php +++ b/src/SentEmails.php @@ -51,6 +51,89 @@ public static function fromEvents(MessageEvents $events): self return new self(...$messages); } + /** + * Get the first email in the collection - fail if none. + * + * @template T + * + * @param class-string $class + * + * @return T + */ + public function first(string $class = TestEmail::class): self + { + return $this->ensureSome()->all($class)[\array_key_first($this->emails)]; + } + + /** + * Get the last email in the collection - fail if none. + * + * @template T + * + * @param class-string $class + * + * @return T + */ + public function last(string $class = TestEmail::class): self + { + return $this->ensureSome()->all($class)[\array_key_last($this->emails)]; + } + + /** + * Perform callback on each email. + * + * @param callable(TestEmail|Email):void $callback {@see TestEmail::call()} + */ + public function each(callable $callback): self + { + foreach ($this as $email) { + $email->call($callback); + } + + return $this; + } + + /** + * Filter collection. + * + * @param callable(TestEmail|Email):bool $filter {@see TestEmail::call()} + */ + public function where(callable $filter): self + { + return new self(...\array_filter($this->emails, $filter)); + } + + public function whereSubject(string $subject): self + { + return $this->where(fn(Email $email) => $email->getSubject() === $subject); + } + + public function whereSubjectContains(string $needle): self + { + return $this->where(fn(Email $email) => str_contains($email->getSubject(), $needle)); + } + + public function whereTag(string $tag): self + { + return $this->where(fn(TestEmail $email) => $email->tag() === $tag); + } + + public function dump(): self + { + \call_user_func(\function_exists('dump') ? 'dump' : 'var_dump', $this->raw()); + + return $this; + } + + /** + * @return never-return + */ + public function dd(): void + { + $this->dump(); + exit(1); + } + /** * @template T * diff --git a/src/TestEmail.php b/src/TestEmail.php index d2a93cc..def1d28 100644 --- a/src/TestEmail.php +++ b/src/TestEmail.php @@ -6,6 +6,8 @@ use Symfony\Component\Mailer\Header\TagHeader; use Symfony\Component\Mime\Email; use Zenstruck\Assert; +use Zenstruck\Callback; +use Zenstruck\Callback\Parameter; /** * @author Kevin Bond @@ -46,6 +48,20 @@ final public function as(string $class): self return new $class($this->inner()); } + /** + * @param callable(TestEmail|Email):mixed $callback + * + * @return mixed + */ + final public function call(callable $callback) + { + return Callback::createFor($callback)->invoke(Parameter::union( + Parameter::untyped($this), + Parameter::typed(Email::class, $this->inner()), + Parameter::typed(self::class, Parameter::factory(fn(string $class) => $this->as($class))) + )); + } + final public function inner(): Email { return $this->email;