From 572a74f0a21ac84665e2c31117be8c1e66fcde36 Mon Sep 17 00:00:00 2001 From: d-mitrofanov-v Date: Sun, 9 Nov 2025 17:33:24 +0200 Subject: [PATCH] add notifier assertions trait --- src/Codeception/Module/Symfony.php | 17 ++ .../Module/Symfony/DataCollectorName.php | 1 + .../Symfony/NotifierAssertionsTrait.php | 253 ++++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 src/Codeception/Module/Symfony/NotifierAssertionsTrait.php diff --git a/src/Codeception/Module/Symfony.php b/src/Codeception/Module/Symfony.php index 0f98cf1..776681e 100644 --- a/src/Codeception/Module/Symfony.php +++ b/src/Codeception/Module/Symfony.php @@ -21,6 +21,7 @@ use Codeception\Module\Symfony\LoggerAssertionsTrait; use Codeception\Module\Symfony\MailerAssertionsTrait; use Codeception\Module\Symfony\MimeAssertionsTrait; +use Codeception\Module\Symfony\NotifierAssertionsTrait; use Codeception\Module\Symfony\ParameterAssertionsTrait; use Codeception\Module\Symfony\RouterAssertionsTrait; use Codeception\Module\Symfony\SecurityAssertionsTrait; @@ -52,6 +53,7 @@ use Symfony\Component\HttpKernel\Profiler\Profile; use Symfony\Component\HttpKernel\Profiler\Profiler; use Symfony\Component\Mailer\DataCollector\MessageDataCollector; +use Symfony\Component\Notifier\DataCollector\NotificationDataCollector; use Symfony\Component\Translation\DataCollector\TranslationDataCollector; use Symfony\Component\VarDumper\Cloner\Data; @@ -156,6 +158,7 @@ class Symfony extends Framework implements DoctrineProvider, PartedModule use LoggerAssertionsTrait; use MailerAssertionsTrait; use MimeAssertionsTrait; + use NotifierAssertionsTrait; use ParameterAssertionsTrait; use RouterAssertionsTrait; use SecurityAssertionsTrait; @@ -412,6 +415,7 @@ protected function getProfile(): ?Profile * ($collector is DataCollectorName::TWIG ? TwigDataCollector : * ($collector is DataCollectorName::SECURITY ? SecurityDataCollector : * ($collector is DataCollectorName::MAILER ? MessageDataCollector : + * ($collector is DataCollectorName::NOTIFIER ? NotificationDataCollector : * DataCollectorInterface * )))))))) * ) @@ -465,6 +469,13 @@ protected function debugResponse(mixed $url): void } } + if ($profile->hasCollector(DataCollectorName::NOTIFIER->value)) { + $notifierCollector = $profile->getCollector(DataCollectorName::NOTIFIER->value); + if ($notifierCollector instanceof NotificationDataCollector) { + $this->debugNotifierData($notifierCollector); + } + } + if ($profile->hasCollector(DataCollectorName::TIME->value)) { $timeCollector = $profile->getCollector(DataCollectorName::TIME->value); if ($timeCollector instanceof TimeDataCollector) { @@ -543,6 +554,12 @@ private function debugMailerData(MessageDataCollector $messageCollector): void $this->debugSection('Emails', sprintf('%d sent', $count)); } + private function debugNotifierData(NotificationDataCollector $notificationCollector): void + { + $count = count($notificationCollector->getEvents()->getMessages()); + $this->debugSection('Notifications', sprintf('%d sent', $count)); + } + private function debugTimeData(TimeDataCollector $timeCollector): void { $this->debugSection('Time', sprintf('%.2f ms', $timeCollector->getDuration())); diff --git a/src/Codeception/Module/Symfony/DataCollectorName.php b/src/Codeception/Module/Symfony/DataCollectorName.php index efa8687..e6eb392 100644 --- a/src/Codeception/Module/Symfony/DataCollectorName.php +++ b/src/Codeception/Module/Symfony/DataCollectorName.php @@ -18,4 +18,5 @@ enum DataCollectorName: string case TWIG = 'twig'; case SECURITY = 'security'; case MAILER = 'mailer'; + case NOTIFIER = 'notifier'; } diff --git a/src/Codeception/Module/Symfony/NotifierAssertionsTrait.php b/src/Codeception/Module/Symfony/NotifierAssertionsTrait.php new file mode 100644 index 0000000..8124f0a --- /dev/null +++ b/src/Codeception/Module/Symfony/NotifierAssertionsTrait.php @@ -0,0 +1,253 @@ +assertNotificationCount(2, 'smtp'); + * ``` + */ + public function assertNotificationCount(int $count, ?string $transportName = null, string $message = ''): void + { + $this->assertThat($this->getNotificationEvents(), new NotifierConstraint\NotificationCount($count, $transportName), $message); + } + + /** + * Asserts that the given notifier event is not queued. + * Use `getNotifierEvent(int $index = 0, ?string $transportName = null)` to retrieve a notifier event by index. + * + * ```php + * getNotifierEvent(); + * $I->asserNotificationIsNotQueued($event); + * ``` + */ + public function assertNotificationIsNotQueued(MessageEvent $event, string $message = ''): void + { + $this->assertThat($event, new LogicalNot(new NotifierConstraint\NotificationIsQueued()), $message); + } + + /** + * Asserts that the given notifier event is queued. + * Use `getNotifierEvent(int $index = 0, ?string $transportName = null)` to retrieve a notifier event by index. + * + * ```php + * getNotifierEvent(); + * $I->assertNotificationlIsQueued($event); + * ``` + */ + public function assertNotificationIsQueued(MessageEvent $event, string $message = ''): void + { + $this->assertThat($event, new NotifierConstraint\NotificationIsQueued(), $message); + } + + /** + * Asserts that the given notification contains given subject. + * Use `getNotifierMessage(int $index = 0, ?string $transportName = null)` to retrieve a notification by index. + * + * ```php + * getNotifierMessage(); + * $I->assertNotificationSubjectContains($notification, 'Subject'); + * ``` + */ + public function assertNotificationSubjectContains(MessageInterface $notification, string $text, string $message = ''): void + { + $bbep = $this->assertThat($notification, new NotifierConstraint\NotificationSubjectContains($text), $message); + $test = $notification; + } + + /** + * Asserts that the given notification does not contain given subject. + * Use `getNotifierMessage(int $index = 0, ?string $transportName = null)` to retrieve a notification by index. + * + * ```php + * getNotifierMessage(); + * $I->assertNotificationSubjectNotContains($notification, 'Subject'); + * ``` + */ + public function assertNotificationSubjectNotContains(MessageInterface $notification, string $text, string $message = ''): void + { + $this->assertThat($notification, new LogicalNot(new NotifierConstraint\NotificationSubjectContains($text)), $message); + } + + /** + * Asserts that the given notification uses given transport. + * Use `getNotifierMessage(int $index = 0, ?string $transportName = null)` to retrieve a notification by index. + * + * ```php + * getNotifierMessage(); + * $I->assertNotificationTransportIsEqual($notification, 'chat'); + * ``` + */ + public function assertNotificationTransportIsEqual(MessageInterface $notification, ?string $transportName = null, string $message = ''): void + { + $this->assertThat($notification, new NotifierConstraint\NotificationTransportIsEqual($transportName), $message); + } + + /** + * Asserts that the given notification does not use given transport. + * Use `getNotifierMessage(int $index = 0, ?string $transportName = null)` to retrieve a notification by index. + * + * ```php + * getNotifierMessage(); + * $I->assertNotificationTransportIsNotEqual($notification, 'transport'); + * ``` + */ + public function assertNotificationTransportIsNotEqual(MessageInterface $notification, ?string $transportName = null, string $message = ''): void + { + $this->assertThat($notification, new LogicalNot(new NotifierConstraint\NotificationTransportIsEqual($transportName)), $message); + } + + /** + * Asserts that the expected number of notifications was queued (e.g. using the Notifier component). + * + * ```php + * assertQueuedNotificationCount(1, 'smtp'); + * ``` + */ + public function assertQueuedNotificationCount(int $count, ?string $transportName = null, string $message = ''): void + { + $this->assertThat($this->getNotificationEvents(), new NotifierConstraint\NotificationCount($count, $transportName, true), $message); + } + + /** + * Checks that no notification was sent. + * The check is based on `\Symfony\Component\Notifier\EventListener\NotificationLoggerListener`, which means: + * If your app performs an HTTP redirect, you need to suppress it using [stopFollowingRedirects()](#stopFollowingRedirects) first; otherwise this check will *always* pass. + * + * ```php + * dontSeeNotificationIsSent(); + * ``` + */ + public function dontSeeNotificationIsSent(): void + { + $this->assertThat($this->getNotificationEvents(), new NotifierConstraint\NotificationCount(0)); + } + + /** + * Returns the last sent notification. + * The check is based on `\Symfony\Component\Notifier\EventListener\NotificationLoggerListener`, which means: + * If your app performs an HTTP redirect after sending the notification, you need to suppress it using [stopFollowingRedirects()](#stopFollowingRedirects) first. + * See also: [grabSentNotifications()](https://codeception.com/docs/modules/Symfony#grabSentNotifications) + * + * ```php + * grabLastSentNotification(); + * $I->assertSame('Subject', $message->getSubject()); + * ``` + */ + public function grabLastSentNotification(): ?MessageInterface + { + $notification = $this->getNotifierMessages(); + $lastNotification = end($notification); + + return $lastNotification ?: null; + } + + + /** + * Returns an array of all sent notifications. + * The check is based on `\Symfony\Component\Notifier\EventListener\NotificationLoggerListener`, which means: + * If your app performs an HTTP redirect after sending the notification, you need to suppress it using [stopFollowingRedirects()](#stopFollowingRedirects) first. + * See also: [grabLastSentNotification()](https://codeception.com/docs/modules/Symfony#grabLastSentNotification) + * + * ```php + * grabSentNotifications(); + * ``` + * + * @return MessageEvent[] + */ + public function grabSentNotifications(): array + { + return $this->getNotifierMessages(); + } + + /** + * Checks if the given number of notifications was sent (default `$expectedCount`: 1). + * The check is based on `\Symfony\Component\Notifier\EventListener\NotificationLoggerListener`, which means: + * If your app performs an HTTP redirect after sending the notification, you need to suppress it using [stopFollowingRedirects()](#stopFollowingRedirects) first. + * + * ```php + * seeNotificatoinIsSent(2); + * ``` + * + * @param int $expectedCount The expected number of notifications sent + */ + public function seeNotificationIsSent(int $expectedCount = 1): void + { + $this->assertThat($this->getNotificationEvents(), new NotifierConstraint\NotificationCount($expectedCount)); + } + + public function getNotifierEvents(?string $transportName = null): array + { + return $this->getNotificationEvents()->getEvents($transportName); + } + + /** + * Returns the notifier event at the specified index. + * + * ```php + * getNotifierEvent(); + * ``` + */ + public function getNotifierEvent(int $index = 0, ?string $transportName = null): ?MessageEvent + { + return $this->getNotifierEvents($transportName)[$index] ?? null; + } + + public function getNotifierMessages(?string $transportName = null): array + { + return $this->getNotificationEvents()->getMessages($transportName); + } + + /** + * Returns the notifier message at the specified index. + * + * ```php + * getNotifierMessage(); + * ``` + */ + public function getNotifierMessage(int $index = 0, ?string $transportName = null): ?MessageInterface + { + return $this->getNotifierMessages($transportName)[$index] ?? null; + } + + protected function getNotificationEvents(): NotificationEvents + { + $notifier = $this->getService('notifier.notification_logger_listener'); + if ($notifier instanceof NotificationLoggerListener) { + return $notifier->getEvents(); + } + $notifier = $this->getService('notifier.logger_notification_listener'); + if ($notifier instanceof NotificationLoggerListener) { + return $notifier->getEvents(); + } + Assert::fail("Notifications can't be tested without Symfony Notifier service."); + } +}