diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 7becd7ca812cd..a7cc899264a0a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 6.2 --- + * Add `NotificationAssertionsTrait` * Add option `framework.catch_all_throwables` to allow `Symfony\Component\HttpKernel\HttpKernel` to catch all kinds of `Throwable` * Make `AbstractController::render()` able to deal with forms and deprecate `renderForm()` * Deprecate the `Symfony\Component\Serializer\Normalizer\ObjectNormalizer` and diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index 165797504bcb8..18b5c21b9f322 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -26,6 +26,7 @@ abstract class KernelTestCase extends TestCase { use MailerAssertionsTrait; + use NotificationAssertionsTrait; protected static $class; diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/NotificationAssertionsTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/NotificationAssertionsTrait.php new file mode 100644 index 0000000000000..f813dcced4785 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Test/NotificationAssertionsTrait.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use PHPUnit\Framework\Constraint\LogicalNot; +use Symfony\Component\Notifier\Event\MessageEvent; +use Symfony\Component\Notifier\Event\NotificationEvents; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Test\Constraint as NotifierConstraint; + +/* + * @author Smaïne Milianni + */ +trait NotificationAssertionsTrait +{ + public static function assertNotificationCount(int $count, string $transport = null, string $message = ''): void + { + self::assertThat(self::getNotificationEvents(), new NotifierConstraint\NotificationCount($count, $transport), $message); + } + + public static function assertQueuedNotificationCount(int $count, string $transport = null, string $message = ''): void + { + self::assertThat(self::getMessageMailerEvents(), new NotifierConstraint\NotificationCount($count, $transport, true), $message); + } + + public static function assertNotificationIsQueued(MessageEvent $event, string $message = ''): void + { + self::assertThat($event, new NotifierConstraint\NotificationIsQueued(), $message); + } + + public static function assertNotificationIsNotQueued(MessageEvent $event, string $message = ''): void + { + self::assertThat($event, new LogicalNot(new NotifierConstraint\NotificationIsQueued()), $message); + } + + public static function assertNotificationSubjectContains(MessageInterface $messageObject, string $text, string $message = ''): void + { + self::assertThat($messageObject, new NotifierConstraint\NotificationSubjectContains($text), $message); + } + + public static function assertNotificationSubjectNotContains(MessageInterface $messageObject, string $text, string $message = ''): void + { + self::assertThat($messageObject, new LogicalNot(new NotifierConstraint\NotificationSubjectContains($text)), $message); + } + + public static function assertNotificationTransportIsEqual(MessageInterface $messageObject, string $text, string $message = ''): void + { + self::assertThat($messageObject, new NotifierConstraint\NotificationTransportIsEqual($text), $message); + } + + public static function assertNotificationTransportIsNotEqual(MessageInterface $messageObject, string $text, string $message = ''): void + { + self::assertThat($messageObject, new LogicalNot(new NotifierConstraint\NotificationTransportIsEqual($text)), $message); + } + + /** + * @return MessageEvent[] + */ + public static function getNotifierEvents(string $transport = null): array + { + return self::getNotificationEvents()->getEvents($transport); + } + + public static function getNotifierEvent(int $index = 0, string $transport = null): ?MessageEvent + { + return self::getNotifierEvents($transport)[$index] ?? null; + } + + /** + * @return MessageInterface[] + */ + public static function getNotifierMessages(string $transport = null): array + { + return self::getNotificationEvents()->getMessages($transport); + } + + public static function getNotifierMessage(int $index = 0, string $transport = null): ?MessageInterface + { + return self::getNotifierMessages($transport)[$index] ?? null; + } + + public static function getNotificationEvents(): NotificationEvents + { + $container = static::getContainer(); + if ($container->has('notifier.logger_notification_listener')) { + return $container->get('notifier.logger_notification_listener')->getEvents(); + } + + static::fail('A client must have Notifier enabled to make notifications assertions. Did you forget to require symfony/notifier?'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/NotificationController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/NotificationController.php new file mode 100644 index 0000000000000..0cdb47c20f40a --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/NotificationController.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Notifier\Notification\Notification; +use Symfony\Component\Notifier\NotifierInterface; + +final class NotificationController +{ + public function indexAction(NotifierInterface $notifier) + { + $firstNotification = new Notification('Hello World!', ['chat/slack']); + $firstNotification->content('Symfony is awesome!'); + + $notifier->send($firstNotification); + + $secondNotification = (new Notification('New urgent notification')) + ->importance(Notification::IMPORTANCE_URGENT) + ; + $notifier->send($secondNotification); + + return new Response(); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml index bfd37826b268b..5630ed621048f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml @@ -64,3 +64,7 @@ send_email: uid: resource: "../../Controller/UidController.php" type: "annotation" + +send_notification: + path: /send_notification + defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\NotificationController::indexAction } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/NotificationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/NotificationTest.php new file mode 100644 index 0000000000000..a565820fc96d6 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/NotificationTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +final class NotificationTest extends AbstractWebTestCase +{ + public function testNotifierAssertion() + { + $client = $this->createClient(['test_case' => 'Notifier', 'root_config' => 'config.yml', 'debug' => true]); + $client->request('GET', '/send_notification'); + + $this->assertNotificationCount(2); + $first = 0; + $second = 1; + $this->assertNotificationIsNotQueued($this->getNotifierEvent($first)); + $this->assertNotificationIsNotQueued($this->getNotifierEvent($second)); + + $notification = $this->getNotifierMessage($first); + $this->assertNotificationSubjectContains($notification, 'Hello World!'); + $this->assertNotificationSubjectNotContains($notification, 'New urgent notification'); + $this->assertNotificationTransportIsEqual($notification, 'slack'); + $this->assertNotificationTransportIsNotEqual($notification, 'mercure'); + + $notification = $this->getNotifierMessage($second); + $this->assertNotificationSubjectContains($notification, 'New urgent notification'); + $this->assertNotificationSubjectNotContains($notification, 'Hello World!'); + $this->assertNotificationTransportIsEqual($notification, 'mercure'); + $this->assertNotificationTransportIsNotEqual($notification, 'slack'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/bundles.php new file mode 100644 index 0000000000000..d50fdea7f582a --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/bundles.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; +use Symfony\Bundle\MercureBundle\MercureBundle; + +return [ + new FrameworkBundle(), + new TestBundle(), + new MercureBundle(), +]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/config.yml new file mode 100644 index 0000000000000..b8f28199a1fb4 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/config.yml @@ -0,0 +1,25 @@ +imports: + - { resource: ../config/default.yml } + - { resource: services.yml } + +framework: + mailer: + dsn: 'null://null' + notifier: + chatter_transports: + slack: 'null://null' + mercure: 'null://null' + channel_policy: + urgent: ['chat/mercure'] + admin_recipients: + - { email: admin@example.com } + profiler: ~ + +mercure: + hubs: + default: + url: 'null://null' + jwt: + secret: '!ChangeMe!' + publish: [ 'foo', 'https://example.com/foo' ] + subscribe: [ 'bar', 'https://example.com/bar' ] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/routing.yml new file mode 100644 index 0000000000000..bd55884d56502 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/routing.yml @@ -0,0 +1,2 @@ +_notifiertest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/services.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/services.yml new file mode 100644 index 0000000000000..d3a7586686058 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Notifier/services.yml @@ -0,0 +1,7 @@ +services: + _defaults: + public: true + + Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\NotificationController: + tags: ['controller.service_arguments'] + diff --git a/src/Symfony/Component/Notifier/CHANGELOG.md b/src/Symfony/Component/Notifier/CHANGELOG.md index 0eeef9f6216b3..f0deefdfe04e2 100644 --- a/src/Symfony/Component/Notifier/CHANGELOG.md +++ b/src/Symfony/Component/Notifier/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.2 +--- + + * Add PHPUnit constraints + 6.1 --- diff --git a/src/Symfony/Component/Notifier/Test/Constraint/NotificationCount.php b/src/Symfony/Component/Notifier/Test/Constraint/NotificationCount.php new file mode 100644 index 0000000000000..d7a5953763de7 --- /dev/null +++ b/src/Symfony/Component/Notifier/Test/Constraint/NotificationCount.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Notifier\Event\NotificationEvents; + +/** + * @author Smaïne Milianni + */ +final class NotificationCount extends Constraint +{ + private $expectedValue; + private $transport; + private $queued; + + public function __construct(int $expectedValue, string $transport = null, bool $queued = false) + { + $this->expectedValue = $expectedValue; + $this->transport = $transport; + $this->queued = $queued; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('%shas %s "%d" emails', $this->transport ? $this->transport.' ' : '', $this->queued ? 'queued' : 'sent', $this->expectedValue); + } + + /** + * @param NotificationEvents $events + * + * {@inheritdoc} + */ + protected function matches($events): bool + { + return $this->expectedValue === $this->countNotifications($events); + } + + /** + * @param NotificationEvents $events + * + * {@inheritdoc} + */ + protected function failureDescription($events): string + { + return sprintf('the Transport %s (%d %s)', $this->toString(), $this->countNotifications($events), $this->queued ? 'queued' : 'sent'); + } + + private function countNotifications(NotificationEvents $events): int + { + $count = 0; + foreach ($events->getEvents($this->transport) as $event) { + if ( + ($this->queued && $event->isQueued()) + || + (!$this->queued && !$event->isQueued()) + ) { + ++$count; + } + } + + return $count; + } +} diff --git a/src/Symfony/Component/Notifier/Test/Constraint/NotificationIsQueued.php b/src/Symfony/Component/Notifier/Test/Constraint/NotificationIsQueued.php new file mode 100644 index 0000000000000..ced1041cbd4a8 --- /dev/null +++ b/src/Symfony/Component/Notifier/Test/Constraint/NotificationIsQueued.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Notifier\Event\MessageEvent; + +/** + * @author Smaïne Milianni + */ +final class NotificationIsQueued extends Constraint +{ + /** + * {@inheritdoc} + */ + public function toString(): string + { + return 'is queued'; + } + + /** + * @param MessageEvent $event + * + * {@inheritdoc} + */ + protected function matches($event): bool + { + return $event->isQueued(); + } + + /** + * @param MessageEvent $event + * + * {@inheritdoc} + */ + protected function failureDescription($event): string + { + return 'the Notification '.$this->toString(); + } +} diff --git a/src/Symfony/Component/Notifier/Test/Constraint/NotificationSubjectContains.php b/src/Symfony/Component/Notifier/Test/Constraint/NotificationSubjectContains.php new file mode 100644 index 0000000000000..a021f895b8341 --- /dev/null +++ b/src/Symfony/Component/Notifier/Test/Constraint/NotificationSubjectContains.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Notifier\Message\MessageInterface; + +/** + * @author Smaïne Milianni + */ +final class NotificationSubjectContains extends Constraint +{ + private $expectedText; + + public function __construct(string $expectedText) + { + $this->expectedText = $expectedText; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('contains "%s"', $this->expectedText); + } + + /** + * {@inheritdoc} + * + * @param MessageInterface $message + */ + protected function matches($message): bool + { + return false !== mb_strpos($message->getSubject(), $this->expectedText); + } + + /** + * {@inheritdoc} + * + * @param MessageInterface $message + */ + protected function failureDescription($message): string + { + return 'the Notification subject '.$this->toString(); + } +} diff --git a/src/Symfony/Component/Notifier/Test/Constraint/NotificationTransportIsEqual.php b/src/Symfony/Component/Notifier/Test/Constraint/NotificationTransportIsEqual.php new file mode 100644 index 0000000000000..9800c2509758d --- /dev/null +++ b/src/Symfony/Component/Notifier/Test/Constraint/NotificationTransportIsEqual.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Notifier\Message\MessageInterface; + +/** + * @author Smaïne Milianni + */ +final class NotificationTransportIsEqual extends Constraint +{ + private $expectedText; + + public function __construct(string $expectedText) + { + $this->expectedText = $expectedText; + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return sprintf('is "%s"', $this->expectedText); + } + + /** + * {@inheritdoc} + * + * @param MessageInterface $message + */ + protected function matches($message): bool + { + return $message->getTransport() === $this->expectedText; + } + + /** + * {@inheritdoc} + * + * @param MessageInterface $message + */ + protected function failureDescription($message): string + { + return 'the Notification transport '.$this->toString(); + } +}