Skip to content

Commit

Permalink
[minor] add static code analysis with phpstan (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbond committed Feb 14, 2022
1 parent d316726 commit e5caa26
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 17 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,22 @@ jobs:

cs-check:
uses: zenstruck/.github/.github/workflows/php-cs-fixer.yml@main

sca:
name: Static Code Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
coverage: none

- name: Install Dependencies
uses: ramsey/composer-install@v1

- name: Run PHPStan
run: vendor/bin/phpstan --error-format=github
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"zenstruck/callback": "^1.1"
},
"require-dev": {
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^9.5.0",
"symfony/messenger": "^4.4|^5.0|^6.0",
"symfony/phpunit-bridge": "^5.3",
Expand Down
92 changes: 92 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
parameters:
ignoreErrors:
-
message: "#^Method Zenstruck\\\\Mailer\\\\Test\\\\SentEmails\\:\\:first\\(\\) should return T of Zenstruck\\\\Mailer\\\\Test\\\\TestEmail but returns Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\.$#"
count: 1
path: src/SentEmails.php

-
message: "#^Method Zenstruck\\\\Mailer\\\\Test\\\\SentEmails\\:\\:last\\(\\) should return T of Zenstruck\\\\Mailer\\\\Test\\\\TestEmail but returns Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\.$#"
count: 1
path: src/SentEmails.php

-
message: "#^Parameter \\#1 \\$filter of method Zenstruck\\\\Mailer\\\\Test\\\\SentEmails\\:\\:where\\(\\) expects callable\\(Symfony\\\\Component\\\\Mime\\\\Email\\|Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\)\\: bool, Closure\\(Symfony\\\\Component\\\\Mime\\\\Email\\)\\: bool given\\.$#"
count: 7
path: src/SentEmails.php

-
message: "#^Parameter \\#1 \\$filter of method Zenstruck\\\\Mailer\\\\Test\\\\SentEmails\\:\\:where\\(\\) expects callable\\(Symfony\\\\Component\\\\Mime\\\\Email\\|Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\)\\: bool, Closure\\(Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\)\\: bool given\\.$#"
count: 1
path: src/SentEmails.php

-
message: "#^Method Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\:\\:as\\(\\) should return T of Zenstruck\\\\Mailer\\\\Test\\\\TestEmail but returns \\$this\\(Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\)\\.$#"
count: 1
path: src/TestEmail.php

-
message: "#^Method Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\:\\:as\\(\\) should return T of Zenstruck\\\\Mailer\\\\Test\\\\TestEmail but returns object\\.$#"
count: 1
path: src/TestEmail.php

-
message: "#^Parameter \\#1 \\$events of static method Zenstruck\\\\Mailer\\\\Test\\\\SentEmails\\:\\:fromEvents\\(\\) expects Symfony\\\\Component\\\\Mailer\\\\Event\\\\MessageEvents, Symfony\\\\Component\\\\Mailer\\\\Event\\\\MessageEvents\\|null given\\.$#"
count: 1
path: src/TestMailer.php

-
message: "#^Cannot call method getBodyAsString\\(\\) on Symfony\\\\Component\\\\Mime\\\\Header\\\\HeaderInterface\\|null\\.$#"
count: 1
path: tests/Fixture/CustomTestEmail.php

-
message: "#^Call to method add\\(\\) on an unknown class Symfony\\\\Component\\\\Routing\\\\RouteCollectionBuilder\\.$#"
count: 2
path: tests/Fixture/Kernel.php

-
message: "#^Class Symfony\\\\Component\\\\Routing\\\\RouteCollectionBuilder not found\\.$#"
count: 1
path: tests/Fixture/Kernel.php

-
message: "#^Parameter \\$routes of method Zenstruck\\\\Mailer\\\\Test\\\\Tests\\\\Fixture\\\\Kernel\\:\\:configureRoutes\\(\\) has invalid type Symfony\\\\Component\\\\Routing\\\\RouteCollectionBuilder\\.$#"
count: 1
path: tests/Fixture/Kernel.php

-
message: "#^Access to an undefined static property Zenstruck\\\\Mailer\\\\Test\\\\Tests\\\\InteractsWithMailerTest\\:\\:\\$container\\.$#"
count: 2
path: tests/InteractsWithMailerTest.php

-
message: "#^Parameter \\#1 \\$class of method Zenstruck\\\\Mailer\\\\Test\\\\SentEmails\\:\\:all\\(\\) expects class\\-string\\<Zenstruck\\\\Mailer\\\\Test\\\\TestEmail\\>, string given\\.$#"
count: 1
path: tests/InteractsWithMailerTest.php

-
message: "#^Access to an undefined static property Zenstruck\\\\Mailer\\\\Test\\\\Tests\\\\NonInteractsWithMailerTest\\:\\:\\$container\\.$#"
count: 1
path: tests/NonInteractsWithMailerTest.php

-
message: "#^Access to an undefined static property Zenstruck\\\\Mailer\\\\Test\\\\Tests\\\\NonKernelTestCaseTest\\:\\:\\$booted\\.$#"
count: 1
path: tests/NonKernelTestCaseTest.php

-
message: "#^Access to an undefined static property Zenstruck\\\\Mailer\\\\Test\\\\Tests\\\\NonKernelTestCaseTest\\:\\:\\$container\\.$#"
count: 1
path: tests/NonKernelTestCaseTest.php

-
message: "#^Call to an undefined static method Zenstruck\\\\Mailer\\\\Test\\\\Tests\\\\NonKernelTestCaseTest\\:\\:getContainer\\(\\)\\.$#"
count: 1
path: tests/NonKernelTestCaseTest.php

-
message: "#^Call to function method_exists\\(\\) with 'Zenstruck\\\\\\\\Mailer\\\\\\\\Test\\\\\\\\Tests\\\\\\\\NonKernelTestCaseTest' and 'getContainer' will always evaluate to false\\.$#"
count: 1
path: tests/NonKernelTestCaseTest.php

8 changes: 8 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
includes:
- phpstan-baseline.neon

parameters:
level: 8
paths:
- src
- tests
6 changes: 5 additions & 1 deletion src/Bridge/Zenstruck/Browser/MailerComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

use Symfony\Component\Mailer\DataCollector\MessageDataCollector;
use Zenstruck\Browser\BrowserKitBrowser;
use Zenstruck\Browser\Component;
use Zenstruck\Mailer\Test\SentEmailMixin;
Expand All @@ -26,6 +27,9 @@ public function sentEmails(): SentEmails
throw new \RuntimeException('The profiler does not include the "mailer" collector. Is symfony/mailer installed?');
}

return SentEmails::fromEvents($browser->profile()->getCollector('mailer')->getEvents());
/** @var MessageDataCollector $collector */
$collector = $browser->profile()->getCollector('mailer');

return SentEmails::fromEvents($collector->getEvents());
}
}
21 changes: 13 additions & 8 deletions src/SentEmails.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

/**
* @author Kevin Bond <kevinbond@gmail.com>
*
* @implements \IteratorAggregate<TestEmail>
*/
final class SentEmails implements \IteratorAggregate, \Countable
{
Expand Down Expand Up @@ -55,9 +57,9 @@ public static function fromEvents(MessageEvents $events): self
/**
* Get the first email in the collection - fail if none.
*
* @template T
* @template T of TestEmail
*
* @param class-string $class
* @param class-string<T> $class
*
* @return T
*/
Expand All @@ -69,9 +71,9 @@ public function first(string $class = TestEmail::class): TestEmail
/**
* Get the last email in the collection - fail if none.
*
* @template T
* @template T of TestEmail
*
* @param class-string $class
* @param class-string<T> $class
*
* @return T
*/
Expand Down Expand Up @@ -111,7 +113,7 @@ public function whereSubject(string $subject): self

public function whereSubjectContains(string $needle): self
{
return $this->where(fn(Email $email) => str_contains($email->getSubject(), $needle));
return $this->where(fn(Email $email) => str_contains((string) $email->getSubject(), $needle));
}

public function whereTag(string $tag): self
Expand Down Expand Up @@ -152,7 +154,7 @@ public function dump(): self
}

/**
* @return never-return
* @return never
*/
public function dd(): void
{
Expand All @@ -161,9 +163,9 @@ public function dd(): void
}

/**
* @template T
* @template T of TestEmail
*
* @param class-string $class
* @param class-string<T> $class
*
* @return TestEmail[]|T[]
*/
Expand Down Expand Up @@ -194,6 +196,9 @@ public function assertNone(): self
return $this->assertCount(0);
}

/**
* @param array<string|int,mixed> $context
*/
public function ensureSome(string $message = 'No emails.', array $context = []): self
{
if (0 === \count($this->emails)) {
Expand Down
36 changes: 28 additions & 8 deletions src/TestEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Symfony\Component\Mailer\Header\TagHeader;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\ParameterizedHeader;
use Zenstruck\Assert;
use Zenstruck\Callback;
use Zenstruck\Callback\Parameter;
Expand All @@ -24,15 +25,20 @@ final public function __construct(Email $email)
$this->email = $email;
}

final public function __call($name, $arguments)
/**
* @param mixed[] $arguments
*
* @return mixed
*/
final public function __call(string $name, array $arguments)
{
return $this->email->{$name}(...$arguments);
}

/**
* @template T
* @template T of self
*
* @param class-string $class
* @param class-string<T> $class
*
* @return T
*/
Expand All @@ -50,16 +56,21 @@ final public function as(string $class): self
}

/**
* @param callable(TestEmail|Email):mixed $callback
* @template T
*
* @return mixed
* @param callable(TestEmail|Email):T $callback
*
* @return T
*/
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)))
Parameter::typed(self::class, Parameter::factory(function(string $class) {
/** @var class-string<TestEmail> $class */
return $this->as($class);
}))
));
}

Expand Down Expand Up @@ -219,12 +230,19 @@ final public function assertTextContains(string $expected): self
final public function assertHasFile(string $expectedFilename, string $expectedContentType, string $expectedContents): self
{
foreach ($this->email->getAttachments() as $attachment) {
if ($expectedFilename !== $attachment->getPreparedHeaders()->get('content-disposition')->getParameter('filename')) {
/** @var ParameterizedHeader $header */
$header = $attachment->getPreparedHeaders()->get('content-disposition');

if ($expectedFilename !== $header->getParameter('filename')) {
continue;
}

Assert::that($attachment->getBody())->is($expectedContents);
Assert::that($attachment->getPreparedHeaders()->get('content-type')->getBodyAsString())

/** @var ParameterizedHeader $header */
$header = $attachment->getPreparedHeaders()->get('content-type');

Assert::that($header->getBodyAsString())
->is($expectedContentType.'; name='.$expectedFilename)
;

Expand Down Expand Up @@ -282,6 +300,8 @@ final public function dd(): void

/**
* @param Address[] $addresses
*
* @return static
*/
private function assertEmail(array $addresses, string $expectedEmail, ?string $expectedName, string $type): self
{
Expand Down
3 changes: 3 additions & 0 deletions tests/EnvironmentProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
*/
trait EnvironmentProvider
{
/**
* @return array<array<string>>
*/
public static function environmentProvider(): iterable
{
yield ['test'];
Expand Down

0 comments on commit e5caa26

Please sign in to comment.