Skip to content

Commit

Permalink
[feature] add SentEmails collection methods/filters
Browse files Browse the repository at this point in the history
  • Loading branch information
kbond committed Oct 7, 2021
1 parent 59b0ce5 commit 20d2635
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 9 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
"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"
},
"require-dev": {
"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"
},
Expand Down
12 changes: 3 additions & 9 deletions src/SentEmailMixin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 <kevinbond@gmail.com>
Expand Down Expand Up @@ -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
*/
Expand All @@ -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;
}
Expand Down
83 changes: 83 additions & 0 deletions src/SentEmails.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
16 changes: 16 additions & 0 deletions src/TestEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 <kevinbond@gmail.com>
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 20d2635

Please sign in to comment.