From 6bf04ab3f03c752b9168b011f2c8d257b06a6a0b Mon Sep 17 00:00:00 2001 From: Gintautas Miselis Date: Mon, 13 Feb 2023 09:21:01 +0200 Subject: [PATCH 1/8] Install PHPUnit 10 for Symfony 6.1 and 6.2 --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ab42b8c5..9cfbda23 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -97,6 +97,11 @@ jobs: run: composer validate working-directory: framework-tests + - name: Install PHPUnit 10 for Symfony 6.1 and 6.2 + if: "matrix.symfony == '6.1.*' || matrix.symfony == '6.2.*'" + run: composer require --dev --no-update "phpunit/phpunit=^10.0" + working-directory: framework-tests + - name: Install Symfony Sample run: | composer remove codeception/codeception codeception/module-asserts codeception/module-doctrine2 codeception/lib-innerbrowser codeception/module-symfony --dev --no-update From de2ac28868cabe493c8863888527d9d6030c7d1a Mon Sep 17 00:00:00 2001 From: Gintautas Miselis Date: Mon, 13 Feb 2023 09:52:04 +0200 Subject: [PATCH 2/8] Install PHPUnit 9 for Symfony 4.4, 5.4 and 6.0 --- .github/workflows/main.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9cfbda23..f0455aaf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -79,6 +79,10 @@ jobs: key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} restore-keys: ${{ runner.os }}-${{ matrix.php }}-composer- + - name: Install PHPUnit 9 for Symfony 4.4, 5.4 and 6.0 + if: "matrix.symfony == '4.4.*' || matrix.symfony == '5.4.*' || matrix.symfony == '6.0.*'" + run: composer require --dev --no-update "phpunit/phpunit=^9.0" + - name: Install dependencies run: | composer require symfony/finder=${{ matrix.symfony }} --no-update @@ -97,7 +101,7 @@ jobs: run: composer validate working-directory: framework-tests - - name: Install PHPUnit 10 for Symfony 6.1 and 6.2 + - name: Install PHPUnit 10 in framework-tests for Symfony 6.1 and 6.2 if: "matrix.symfony == '6.1.*' || matrix.symfony == '6.2.*'" run: composer require --dev --no-update "phpunit/phpunit=^10.0" working-directory: framework-tests From beccd5e1366510df39e6055e60af9d03c63fc1d3 Mon Sep 17 00:00:00 2001 From: Mykhailo Sverdlykivskyi Date: Sat, 18 Feb 2023 17:56:51 +0200 Subject: [PATCH 3/8] Deprecate event triggered assertions (#169) --- .../Module/Symfony/EventsAssertionsTrait.php | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php index f4b47b98..5a20be5b 100644 --- a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php @@ -56,8 +56,27 @@ public function dontSeeOrphanEvent(array|object|string $expected = null): void * ``` * * @param object|string|string[] $expected + * @deprecated Use `dontSeeEventListenerIsCalled()` instead. */ public function dontSeeEventTriggered(array|object|string $expected): void + { + trigger_error('dontSeeEventTriggered is deprecated, please use dontSeeEventListenerIsCalled instead', E_USER_DEPRECATED); + $this->dontSeeEventListenerIsCalled($expected); + } + + /** + * Verifies that one or more event listeners were not called during the test. + * + * ```php + * dontSeeEventListenerIsCalled('App\MyEventListener'); + * $I->dontSeeEventListenerIsCalled(new App\Events\MyEventListener()); + * $I->dontSeeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']); + * ``` + * + * @param object|string|string[] $expected + */ + public function dontSeeEventListenerIsCalled(array|object|string $expected): void { $eventCollector = $this->grabEventCollector(__FUNCTION__); @@ -106,8 +125,27 @@ public function seeOrphanEvent(array|object|string $expected): void * ``` * * @param object|string|string[] $expected + * @deprecated Use `seeEventListenerIsCalled()` instead. */ public function seeEventTriggered(array|object|string $expected): void + { + trigger_error('seeEventTriggered is deprecated, please use seeEventListenerIsCalled instead', E_USER_DEPRECATED); + $this->seeEventListenerIsCalled($expected); + } + + /** + * Verifies that one or more event listeners were called during the test. + * + * ```php + * seeEventListenerIsCalled('App\MyEventListener'); + * $I->seeEventListenerIsCalled(new App\Events\MyEventListener()); + * $I->seeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']); + * ``` + * + * @param object|string|string[] $expected + */ + public function seeEventListenerIsCalled(array|object|string $expected): void { $eventCollector = $this->grabEventCollector(__FUNCTION__); @@ -170,4 +208,4 @@ protected function grabEventCollector(string $function): EventDataCollector { return $this->grabCollector('events', $function); } -} \ No newline at end of file +} From cb1334090161aecc94bdb78c7ea3a52417cd728b Mon Sep 17 00:00:00 2001 From: Aaron Nieves <64917965+TavoNiievez@users.noreply.github.com> Date: Mon, 23 Oct 2023 13:28:50 -0500 Subject: [PATCH 4/8] Various improvements (#171) * Traits improvements * Optimize imports --- .../Module/Symfony/BrowserAssertionsTrait.php | 1 - .../Module/Symfony/ConsoleAssertionsTrait.php | 8 ++- .../Symfony/DoctrineAssertionsTrait.php | 2 +- .../Module/Symfony/EventsAssertionsTrait.php | 6 -- .../Module/Symfony/FormAssertionsTrait.php | 4 +- .../Module/Symfony/MailerAssertionsTrait.php | 8 +-- .../Symfony/ParameterAssertionsTrait.php | 6 +- .../Module/Symfony/RouterAssertionsTrait.php | 10 +-- .../Symfony/ServicesAssertionsTrait.php | 12 +--- .../Module/Symfony/SessionAssertionsTrait.php | 71 ++++++++----------- .../Module/Symfony/TimeAssertionsTrait.php | 2 +- .../Module/Symfony/TwigAssertionsTrait.php | 8 +-- 12 files changed, 51 insertions(+), 87 deletions(-) diff --git a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php index cabd34f2..67dc1ddb 100644 --- a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php @@ -23,7 +23,6 @@ trait BrowserAssertionsTrait * // Perform other requests * * ``` - * */ public function rebootClientKernel(): void { diff --git a/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php b/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php index d54e2bfa..66edec9e 100644 --- a/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php @@ -40,8 +40,12 @@ public function runSymfonyConsoleCommand(string $command, array $parameters = [] $this->assertSame( $expectedExitCode, $exitCode, - 'Command did not exit with code ' . $expectedExitCode - . ' but with ' . $exitCode . ': ' . $output + sprintf( + 'Command did not exit with code %d but with %d: %s', + $expectedExitCode, + $exitCode, + $output + ) ); return $output; diff --git a/src/Codeception/Module/Symfony/DoctrineAssertionsTrait.php b/src/Codeception/Module/Symfony/DoctrineAssertionsTrait.php index 9aa4a054..6f9f0bc4 100644 --- a/src/Codeception/Module/Symfony/DoctrineAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/DoctrineAssertionsTrait.php @@ -6,7 +6,6 @@ use Doctrine\ORM\EntityRepository; use function class_exists; -use function get_class; use function interface_exists; use function is_object; use function is_string; @@ -66,6 +65,7 @@ public function grabRepository(object|string $mixed): ?EntityRepository $getRepo = function () use ($mixed, $entityRepoClass, $isNotARepo): ?EntityRepository { if (!$repo = $this->grabService($mixed)) return null; + /** @var EntityRepository $repo */ if (!$repo instanceof $entityRepoClass) { $isNotARepo(); return null; diff --git a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php index 5a20be5b..04c3d081 100644 --- a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php @@ -6,10 +6,8 @@ use Symfony\Component\HttpKernel\DataCollector\EventDataCollector; use Symfony\Component\VarDumper\Cloner\Data; -use function get_class; use function is_array; use function is_object; -use function strpos; trait EventsAssertionsTrait { @@ -34,7 +32,6 @@ public function dontSeeOrphanEvent(array|object|string $expected = null): void { $eventCollector = $this->grabEventCollector(__FUNCTION__); - /** @var Data $data */ $data = $eventCollector->getOrphanedEvents(); $expected = is_array($expected) ? $expected : [$expected]; @@ -80,7 +77,6 @@ public function dontSeeEventListenerIsCalled(array|object|string $expected): voi { $eventCollector = $this->grabEventCollector(__FUNCTION__); - /** @var Data $data */ $data = $eventCollector->getCalledListeners(); $expected = is_array($expected) ? $expected : [$expected]; @@ -107,7 +103,6 @@ public function seeOrphanEvent(array|object|string $expected): void { $eventCollector = $this->grabEventCollector(__FUNCTION__); - /** @var Data $data */ $data = $eventCollector->getOrphanedEvents(); $expected = is_array($expected) ? $expected : [$expected]; @@ -149,7 +144,6 @@ public function seeEventListenerIsCalled(array|object|string $expected): void { $eventCollector = $this->grabEventCollector(__FUNCTION__); - /** @var Data $data */ $data = $eventCollector->getCalledListeners(); $expected = is_array($expected) ? $expected : [$expected]; diff --git a/src/Codeception/Module/Symfony/FormAssertionsTrait.php b/src/Codeception/Module/Symfony/FormAssertionsTrait.php index 8261ea9f..31940e15 100644 --- a/src/Codeception/Module/Symfony/FormAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/FormAssertionsTrait.php @@ -51,7 +51,7 @@ public function seeFormErrorMessage(string $field, string $message = null): void $formCollector = $this->grabFormCollector(__FUNCTION__); if (!$forms = $formCollector->getData()->getValue(true)['forms']) { - $this->fail('There are no forms on the current page.'); + $this->fail('No forms found on the current page.'); } $fields = []; @@ -73,7 +73,7 @@ public function seeFormErrorMessage(string $field, string $message = null): void } if (!in_array($field, $fields)) { - $this->fail("the field '{$field}' does not exist in the form."); + $this->fail("The field '{$field}' does not exist in the form."); } if (!array_key_exists($field, $errors)) { diff --git a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php index cb82484a..4e61cb2d 100644 --- a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php @@ -56,13 +56,11 @@ public function seeEmailIsSent(int $expectedCount = 1): void */ public function grabLastSentEmail(): ?Email { + /** @var Email[] $emails */ $emails = $this->getMessageMailerEvents()->getMessages(); - /** @var Email|false $lastEmail */ - if ($lastEmail = end($emails)) { - return $lastEmail; - } + $lastEmail = end($emails); - return null; + return $lastEmail ?: null; } /** diff --git a/src/Codeception/Module/Symfony/ParameterAssertionsTrait.php b/src/Codeception/Module/Symfony/ParameterAssertionsTrait.php index cb5bcad2..63231dd5 100644 --- a/src/Codeception/Module/Symfony/ParameterAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ParameterAssertionsTrait.php @@ -16,13 +16,13 @@ trait ParameterAssertionsTrait * $I->grabParameter('app.business_name'); * ``` * - * @param string $name + * @param string $parameterName * @return array|bool|float|int|string|null */ - public function grabParameter(string $name) + public function grabParameter(string $parameterName) { $parameterBag = $this->grabParameterBagService(); - return $parameterBag->get($name); + return $parameterBag->get($parameterName); } protected function grabParameterBagService(): ParameterBagInterface diff --git a/src/Codeception/Module/Symfony/RouterAssertionsTrait.php b/src/Codeception/Module/Symfony/RouterAssertionsTrait.php index c023782f..e0bdeab0 100644 --- a/src/Codeception/Module/Symfony/RouterAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/RouterAssertionsTrait.php @@ -11,8 +11,6 @@ use function array_merge; use function explode; use function sprintf; -use function strlen; -use function substr_compare; trait RouterAssertionsTrait { @@ -32,7 +30,6 @@ trait RouterAssertionsTrait public function amOnAction(string $action, array $params = []): void { $router = $this->grabRouterService(); - $routes = $router->getRouteCollection()->getIterator(); foreach ($routes as $route) { @@ -66,7 +63,7 @@ public function amOnRoute(string $routeName, array $params = []): void { $router = $this->grabRouterService(); if ($router->getRouteCollection()->get($routeName) === null) { - $this->fail(sprintf('Route with name "%s" does not exists.', $routeName)); + $this->fail(sprintf('Route with name "%s" does not exist.', $routeName)); } $url = $router->generate($routeName, $params); @@ -95,7 +92,6 @@ public function invalidateCachedRouter(): void public function seeCurrentActionIs(string $action): void { $router = $this->grabRouterService(); - $routes = $router->getRouteCollection()->getIterator(); foreach ($routes as $route) { @@ -128,7 +124,7 @@ public function seeCurrentRouteIs(string $routeName, array $params = []): void { $router = $this->grabRouterService(); if ($router->getRouteCollection()->get($routeName) === null) { - $this->fail(sprintf('Route with name "%s" does not exists.', $routeName)); + $this->fail(sprintf('Route with name "%s" does not exist.', $routeName)); } $uri = explode('?', $this->grabFromCurrentUrl())[0]; @@ -160,7 +156,7 @@ public function seeInCurrentRoute(string $routeName): void { $router = $this->grabRouterService(); if ($router->getRouteCollection()->get($routeName) === null) { - $this->fail(sprintf('Route with name "%s" does not exists.', $routeName)); + $this->fail(sprintf('Route with name "%s" does not exist.', $routeName)); } $uri = explode('?', $this->grabFromCurrentUrl())[0]; diff --git a/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php b/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php index b53a72b8..bd9140c0 100644 --- a/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php @@ -20,7 +20,6 @@ trait ServicesAssertionsTrait * ``` * * @part services - * @param string $serviceId */ public function grabService(string $serviceId): object { @@ -69,15 +68,10 @@ public function persistPermanentService(string $serviceName): void */ public function unpersistService(string $serviceName): void { - if (isset($this->persistentServices[$serviceName])) { - unset($this->persistentServices[$serviceName]); - } - - if (isset($this->permanentServices[$serviceName])) { - unset($this->permanentServices[$serviceName]); - } + unset($this->persistentServices[$serviceName]); + unset($this->permanentServices[$serviceName]); - if ($this->client instanceof SymfonyConnector && isset($this->client->persistentServices[$serviceName])) { + if ($this->client instanceof SymfonyConnector) { unset($this->client->persistentServices[$serviceName]); } } diff --git a/src/Codeception/Module/Symfony/SessionAssertionsTrait.php b/src/Codeception/Module/Symfony/SessionAssertionsTrait.php index f6911303..a8b69afd 100644 --- a/src/Codeception/Module/Symfony/SessionAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/SessionAssertionsTrait.php @@ -29,37 +29,17 @@ trait SessionAssertionsTrait * ]); * $I->amLoggedInAs($user); * ``` - * - * @param UserInterface $user - * @param string $firewallName - * @param null $firewallContext */ - public function amLoggedInAs(UserInterface $user, string $firewallName = 'main', $firewallContext = null): void + public function amLoggedInAs(UserInterface $user, string $firewallName = 'main', string $firewallContext = null): void { $session = $this->getCurrentSession(); + $roles = $user->getRoles(); - if ($this->getSymfonyMajorVersion() < 6) { - if ($this->config['guard']) { - $token = new PostAuthenticationGuardToken($user, $firewallName, $user->getRoles()); - } else { - $token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles()); - } - } else { - if ($this->config['authenticator']) { - $token = new PostAuthenticationToken($user, $firewallName, $user->getRoles()); - } else { - $token = new UsernamePasswordToken($user, $firewallName, $user->getRoles()); - } - } - + $token = $this->createAuthenticationToken($user, $firewallName, $roles); $this->getTokenStorage()->setToken($token); - if ($firewallContext) { - $session->set('_security_' . $firewallContext, serialize($token)); - } else { - $session->set('_security_' . $firewallName, serialize($token)); - } - + $sessionKey = $firewallContext ? "_security_{$firewallContext}" : "_security_{$firewallName}"; + $session->set($sessionKey, serialize($token)); $session->save(); $cookie = new Cookie($session->getName(), $session->getId()); @@ -74,16 +54,13 @@ public function amLoggedInAs(UserInterface $user, string $firewallName = 'main', * $I->dontSeeInSession('attribute'); * $I->dontSeeInSession('attribute', 'value'); * ``` - * */ public function dontSeeInSession(string $attribute, mixed $value = null): void { $session = $this->getCurrentSession(); - if ($attributeExists = $session->has($attribute)) { - $this->fail("Session attribute with name '{$attribute}' does exist"); - } - $this->assertFalse($attributeExists); + $attributeExists = $session->has($attribute); + $this->assertFalse($attributeExists, "Session attribute '{$attribute}' exists."); if (null !== $value) { $this->assertNotSame($value, $session->get($attribute)); @@ -98,8 +75,7 @@ public function dontSeeInSession(string $attribute, mixed $value = null): void */ public function goToLogoutPath(): void { - $logoutUrlGenerator = $this->getLogoutUrlGenerator(); - $logoutPath = $logoutUrlGenerator->getLogoutPath(); + $logoutPath = $this->getLogoutUrlGenerator()->getLogoutPath(); $this->amOnPage($logoutPath); } @@ -132,17 +108,14 @@ public function logoutProgrammatically(): void } $session = $this->getCurrentSession(); - $sessionName = $session->getName(); $session->invalidate(); $cookieJar = $this->client->getCookieJar(); + $cookiesToExpire = ['MOCKSESSID', 'REMEMBERME', $sessionName]; foreach ($cookieJar->all() as $cookie) { $cookieName = $cookie->getName(); - if ($cookieName === 'MOCKSESSID' || - $cookieName === 'REMEMBERME' || - $cookieName === $sessionName - ) { + if (in_array($cookieName, $cookiesToExpire, true)) { $cookieJar->expire($cookieName); } } @@ -163,10 +136,8 @@ public function seeInSession(string $attribute, mixed $value = null): void { $session = $this->getCurrentSession(); - if (!$attributeExists = $session->has($attribute)) { - $this->fail("No session attribute with name '{$attribute}'"); - } - $this->assertTrue($attributeExists); + $attributeExists = $session->has($attribute); + $this->assertTrue($attributeExists, "No session attribute with name '{$attribute}'"); if (null !== $value) { $this->assertSame($value, $session->get($attribute)); @@ -181,8 +152,6 @@ public function seeInSession(string $attribute, mixed $value = null): void * $I->seeSessionHasValues(['key1', 'key2']); * $I->seeSessionHasValues(['key1' => 'value1', 'key2' => 'value2']); * ``` - * - * @param array $bindings */ public function seeSessionHasValues(array $bindings): void { @@ -227,4 +196,20 @@ protected function getSymfonyMajorVersion(): int { return $this->kernel::MAJOR_VERSION; } + + /** + * @return UsernamePasswordToken|PostAuthenticationGuardToken|PostAuthenticationToken + */ + protected function createAuthenticationToken(UserInterface $user, string $firewallName, array $roles) + { + if ($this->getSymfonyMajorVersion() < 6) { + return $this->config['guard'] + ? new PostAuthenticationGuardToken($user, $firewallName, $roles) + : new UsernamePasswordToken($user, null, $firewallName, $roles); + } + + return $this->config['authenticator'] + ? new PostAuthenticationToken($user, $firewallName, $roles) + : new UsernamePasswordToken($user, $firewallName, $roles); + } } diff --git a/src/Codeception/Module/Symfony/TimeAssertionsTrait.php b/src/Codeception/Module/Symfony/TimeAssertionsTrait.php index d48222eb..136bef25 100644 --- a/src/Codeception/Module/Symfony/TimeAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TimeAssertionsTrait.php @@ -36,7 +36,7 @@ public function seeRequestTimeIsLessThan(int|float $expectedMilliseconds): void $expectedMilliseconds, $actualMilliseconds, sprintf( - 'The request was expected to last less than %d ms, but it actually lasted %d ms.', + 'The request duration was expected to be less than %d ms, but it was actually %d ms.', $expectedMilliseconds, $actualMilliseconds ) diff --git a/src/Codeception/Module/Symfony/TwigAssertionsTrait.php b/src/Codeception/Module/Symfony/TwigAssertionsTrait.php index 624b822e..52b02d0d 100644 --- a/src/Codeception/Module/Symfony/TwigAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TwigAssertionsTrait.php @@ -16,8 +16,6 @@ trait TwigAssertionsTrait * dontSeeRenderedTemplate('home.html.twig'); * ``` - * - * @param string $template */ public function dontSeeRenderedTemplate(string $template): void { @@ -39,15 +37,13 @@ public function dontSeeRenderedTemplate(string $template): void * seeCurrentTemplateIs('home.html.twig'); * ``` - * - * @param string $expectedTemplate */ public function seeCurrentTemplateIs(string $expectedTemplate): void { $twigCollector = $this->grabTwigCollector(__FUNCTION__); $templates = (array)$twigCollector->getTemplates(); - $actualTemplate = (string)array_key_first($templates); + $actualTemplate = !empty($templates) ? (string) array_key_first($templates) : 'N/A'; $this->assertSame( $expectedTemplate, @@ -65,8 +61,6 @@ public function seeCurrentTemplateIs(string $expectedTemplate): void * $I->seeRenderedTemplate('home.html.twig'); * $I->seeRenderedTemplate('layout.html.twig'); * ``` - * - * @param string $template */ public function seeRenderedTemplate(string $template): void { From 19c86fc1041fc29b151ec9a783426089731dbae7 Mon Sep 17 00:00:00 2001 From: Mykhailo Sverdlykivskyi Date: Mon, 11 Dec 2023 21:10:21 +0000 Subject: [PATCH 5/8] Update event assertions (#168) * Add event dispatch assertions, event listener call assertions (deprecate existing ones) * fix wrong method call * Remove duplicated methods, fix PHPCS warnings * Restore lost deprecation warnings, reorder methods to keep in line with upstream * Update seeEventListenerCalled/dontSeeEventListenerCalled to support check with event --------- Co-authored-by: TavoNiievez --- .../Module/Symfony/EventsAssertionsTrait.php | 99 +++++++++++++++++-- 1 file changed, 89 insertions(+), 10 deletions(-) diff --git a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php index 04c3d081..81381a05 100644 --- a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php @@ -6,6 +6,7 @@ use Symfony\Component\HttpKernel\DataCollector\EventDataCollector; use Symfony\Component\VarDumper\Cloner\Data; + use function is_array; use function is_object; @@ -53,11 +54,14 @@ public function dontSeeOrphanEvent(array|object|string $expected = null): void * ``` * * @param object|string|string[] $expected - * @deprecated Use `dontSeeEventListenerIsCalled()` instead. + * @deprecated Use `dontSeeEventListenerIsCalled` instead. */ public function dontSeeEventTriggered(array|object|string $expected): void { - trigger_error('dontSeeEventTriggered is deprecated, please use dontSeeEventListenerIsCalled instead', E_USER_DEPRECATED); + trigger_error( + 'dontSeeEventTriggered is deprecated, please use dontSeeEventListenerIsCalled instead', + E_USER_DEPRECATED + ); $this->dontSeeEventListenerIsCalled($expected); } @@ -69,18 +73,28 @@ public function dontSeeEventTriggered(array|object|string $expected): void * $I->dontSeeEventListenerIsCalled('App\MyEventListener'); * $I->dontSeeEventListenerIsCalled(new App\Events\MyEventListener()); * $I->dontSeeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']); + * $I->dontSeeEventListenerIsCalled('App\MyEventListener', 'my.event); + * $I->dontSeeEventListenerIsCalled(new App\Events\MyEventListener(), new MyEvent()); + * $I->dontSeeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']); * ``` * * @param object|string|string[] $expected */ - public function dontSeeEventListenerIsCalled(array|object|string $expected): void - { + public function dontSeeEventListenerIsCalled( + array|object|string $expected, + array|object|string $withEvents = [] + ): void { $eventCollector = $this->grabEventCollector(__FUNCTION__); $data = $eventCollector->getCalledListeners(); $expected = is_array($expected) ? $expected : [$expected]; + $withEvents = is_array($withEvents) ? $withEvents : [$withEvents]; + + if (!empty($withEvents) && count($expected) > 1) { + $this->fail('You cannot check for events when using multiple listeners. Make multiple assertions instead.'); + } - $this->assertEventNotTriggered($data, $expected); + $this->assertListenerCalled($data, $expected, $withEvents, true); } /** @@ -120,11 +134,14 @@ public function seeOrphanEvent(array|object|string $expected): void * ``` * * @param object|string|string[] $expected - * @deprecated Use `seeEventListenerIsCalled()` instead. + * @deprecated Use `seeEventListenerIsCalled` instead. */ public function seeEventTriggered(array|object|string $expected): void { - trigger_error('seeEventTriggered is deprecated, please use seeEventListenerIsCalled instead', E_USER_DEPRECATED); + trigger_error( + 'seeEventTriggered is deprecated, please use seeEventListenerIsCalled instead', + E_USER_DEPRECATED + ); $this->seeEventListenerIsCalled($expected); } @@ -136,18 +153,28 @@ public function seeEventTriggered(array|object|string $expected): void * $I->seeEventListenerIsCalled('App\MyEventListener'); * $I->seeEventListenerIsCalled(new App\Events\MyEventListener()); * $I->seeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']); + * $I->seeEventListenerIsCalled('App\MyEventListener', 'my.event); + * $I->seeEventListenerIsCalled(new App\Events\MyEventListener(), new MyEvent()); + * $I->seeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']); * ``` * * @param object|string|string[] $expected */ - public function seeEventListenerIsCalled(array|object|string $expected): void - { + public function seeEventListenerIsCalled( + array|object|string $expected, + array|object|string $withEvents = [] + ): void { $eventCollector = $this->grabEventCollector(__FUNCTION__); $data = $eventCollector->getCalledListeners(); $expected = is_array($expected) ? $expected : [$expected]; + $withEvents = is_array($withEvents) ? $withEvents : [$withEvents]; - $this->assertEventTriggered($data, $expected); + if (!empty($withEvents) && count($expected) > 1) { + $this->fail('You cannot check for events when using multiple listeners. Make multiple assertions instead.'); + } + + $this->assertListenerCalled($data, $expected, $withEvents); } protected function assertEventNotTriggered(Data $data, array $expected): void @@ -180,6 +207,39 @@ protected function assertEventTriggered(Data $data, array $expected): void } } + protected function assertListenerCalled( + Data $data, + array $expectedListeners, + array $withEvents, + bool $invertAssertion = false + ): void { + $assertTrue = !$invertAssertion; + + if ($assertTrue && $data->count() === 0) { + $this->fail('No event listener was called'); + } + + $actual = $data->getValue(true); + $expectedEvents = empty($withEvents) ? [null] : $withEvents; + + foreach ($expectedListeners as $expectedListener) { + $expectedListener = is_object($expectedListener) ? $expectedListener::class : $expectedListener; + + foreach ($expectedEvents as $expectedEvent) { + $message = "The '{$expectedListener}' listener was called" + . ($expectedEvent ? " for the '{$expectedEvent}' event" : ''); + + $condition = $this->listenerWasCalled($actual, $expectedListener, $expectedEvent); + + if ($assertTrue) { + $this->assertTrue($condition, $message); + } else { + $this->assertFalse($condition, $message); + } + } + } + } + protected function eventWasTriggered(array $actual, string $expectedEvent): bool { $triggered = false; @@ -195,9 +255,28 @@ protected function eventWasTriggered(array $actual, string $expectedEvent): bool } } } + return $triggered; } + protected function listenerWasCalled(array $actual, string $expectedListener, string|null $expectedEvent): bool + { + $called = false; + + foreach ($actual as $actualEvent) { + // Called Listeners + if (is_array($actualEvent) && str_starts_with($actualEvent['pretty'], $expectedListener)) { + if ($expectedEvent === null) { + $called = true; + } elseif ($actualEvent['event'] === $expectedEvent) { + $called = true; + } + } + } + + return $called; + } + protected function grabEventCollector(string $function): EventDataCollector { return $this->grabCollector('events', $function); From 6c8b03cc8d07c0bfeb37a73ddce960ce237406bf Mon Sep 17 00:00:00 2001 From: Mykhailo Sverdlykivskyi Date: Wed, 20 Dec 2023 15:30:55 +0200 Subject: [PATCH 6/8] Add seeEvent/dontSeeEvent event assertions (#173) * Add seeEvent/dontSeeEvent --- .../Module/Symfony/EventsAssertionsTrait.php | 117 ++++++++++++++---- 1 file changed, 91 insertions(+), 26 deletions(-) diff --git a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php index 81381a05..f6d56cc3 100644 --- a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php @@ -39,7 +39,40 @@ public function dontSeeOrphanEvent(array|object|string $expected = null): void if ($expected === null) { $this->assertSame(0, $data->count()); } else { - $this->assertEventNotTriggered($data, $expected); + $this->assertEventTriggered($data, $expected, true); + } + } + + /** + * Verifies that there were no events during the test. + * Both regular and orphan events are checked. + * + * ```php + * dontSeeEvent(); + * $I->dontSeeEvent('App\MyEvent'); + * $I->dontSeeEvent(new App\Events\MyEvent()); + * $I->dontSeeEvent(['App\MyEvent', 'App\MyOtherEvent']); + * ``` + * + * @param array|object|string|null $expected + */ + public function dontSeeEvent(array|object|string $expected = null): void + { + $eventCollector = $this->grabEventCollector(__FUNCTION__); + + $data = [ + $eventCollector->getOrphanedEvents(), + $eventCollector->getCalledListeners(), + ]; + $expected = is_array($expected) ? $expected : [$expected]; + + if ($expected === null) { + foreach ($data as $dataItem) { + $this->assertSame(0, $dataItem->count()); + } + } else { + $this->assertEventTriggered($data, $expected, true); } } @@ -123,6 +156,35 @@ public function seeOrphanEvent(array|object|string $expected): void $this->assertEventTriggered($data, $expected); } + /** + * Verifies that one or more events were dispatched during the test. + * Both regular and orphan events are checked. + * + * If you need to verify that expected event is not orphan, + * add `dontSeeOrphanEvent` call. + * + * ```php + * seeEvent('App\MyEvent'); + * $I->seeEvent(new App\Events\MyEvent()); + * $I->seeEvent(['App\MyEvent', 'App\MyOtherEvent']); + * ``` + * + * @param array|object|string $expected + */ + public function seeEvent(array|object|string $expected): void + { + $eventCollector = $this->grabEventCollector(__FUNCTION__); + + $data = [ + $eventCollector->getOrphanedEvents(), + $eventCollector->getCalledListeners(), + ]; + $expected = is_array($expected) ? $expected : [$expected]; + + $this->assertEventTriggered($data, $expected); + } + /** * Verifies that one or more event listeners were called during the test. * @@ -177,33 +239,38 @@ public function seeEventListenerIsCalled( $this->assertListenerCalled($data, $expected, $withEvents); } - protected function assertEventNotTriggered(Data $data, array $expected): void - { - $actual = $data->getValue(true); - - foreach ($expected as $expectedEvent) { - $expectedEvent = is_object($expectedEvent) ? $expectedEvent::class : $expectedEvent; - $this->assertFalse( - $this->eventWasTriggered($actual, (string)$expectedEvent), - "The '{$expectedEvent}' event triggered" - ); - } - } + protected function assertEventTriggered( + array|Data $data, + array $expected, + bool $invertAssertion = false + ): void { + $assertTrue = !$invertAssertion; + $data = is_array($data) ? $data : [$data]; + $totalEvents = array_sum(array_map('count', $data)); - protected function assertEventTriggered(Data $data, array $expected): void - { - if ($data->count() === 0) { + if ($assertTrue && $totalEvents === 0) { $this->fail('No event was triggered'); } - $actual = $data->getValue(true); + $actualEventsCollection = array_map(static fn (Data $data) => $data->getValue(true), $data); foreach ($expected as $expectedEvent) { $expectedEvent = is_object($expectedEvent) ? $expectedEvent::class : $expectedEvent; - $this->assertTrue( - $this->eventWasTriggered($actual, (string)$expectedEvent), - "The '{$expectedEvent}' event did not trigger" - ); + $message = $assertTrue + ? "The '{$expectedEvent}' event did not trigger" + : "The '{$expectedEvent}' event triggered"; + + $eventTriggered = false; + + foreach ($actualEventsCollection as $actualEvents) { + $eventTriggered = $eventTriggered || $this->eventWasTriggered($actualEvents, (string)$expectedEvent); + } + + if ($assertTrue) { + $this->assertTrue($eventTriggered, $message); + } else { + $this->assertFalse($eventTriggered, $message); + } } } @@ -246,13 +313,11 @@ protected function eventWasTriggered(array $actual, string $expectedEvent): bool foreach ($actual as $actualEvent) { if (is_array($actualEvent)) { // Called Listeners - if (str_starts_with($actualEvent['pretty'], $expectedEvent)) { - $triggered = true; - } - } else { // Orphan Events - if ($actualEvent === $expectedEvent) { + if ($actualEvent['event'] === $expectedEvent) { $triggered = true; } + } elseif ($actualEvent === $expectedEvent) { // Orphan Events + $triggered = true; } } From fb80fccef4f1de982ba7d75aaa1143d43938da13 Mon Sep 17 00:00:00 2001 From: Tavo Nieves J Date: Fri, 22 Dec 2023 23:02:00 -0500 Subject: [PATCH 7/8] Support Symfony 6.3 --- .github/workflows/main.yml | 14 ++++++++++++-- composer.json | 18 +++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f0455aaf..885dd616 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,12 +9,14 @@ jobs: strategy: matrix: php: [8.0, 8.1, 8.2] - symfony: ["4.4.*", "5.4.*", "6.0.*", "6.1.*", "6.2.*"] + symfony: ["4.4.*", "5.4.*", "6.0.*", "6.1.*", "6.2.*", "6.3.*"] exclude: - php: 8.0 symfony: "6.1.*" - php: 8.0 symfony: "6.2.*" + - php: 8.0 + symfony: "6.3.*" steps: - name: Checkout code @@ -68,6 +70,14 @@ jobs: path: framework-tests ref: "6.2" + - name: Checkout Symfony 6.3 Sample + if: "matrix.symfony == '6.3.*'" + uses: actions/checkout@v2 + with: + repository: Codeception/symfony-module-tests + path: framework-tests + ref: "6.3" + - name: Get composer cache directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" @@ -102,7 +112,7 @@ jobs: working-directory: framework-tests - name: Install PHPUnit 10 in framework-tests for Symfony 6.1 and 6.2 - if: "matrix.symfony == '6.1.*' || matrix.symfony == '6.2.*'" + if: "matrix.symfony == '6.1.*' || matrix.symfony == '6.2.*' || matrix.symfony == '6.3.*'" run: composer require --dev --no-update "phpunit/phpunit=^10.0" working-directory: framework-tests diff --git a/composer.json b/composer.json index 1e03edcd..24d5822d 100644 --- a/composer.json +++ b/composer.json @@ -20,20 +20,36 @@ "require": { "php": "^8.0", "ext-json": "*", - "codeception/codeception": "^5.0.0-RC3", + "codeception/codeception": "^5.0.8", "codeception/lib-innerbrowser": "^3.1.1 | ^4.0" }, "require-dev": { "codeception/module-asserts": "^3.0", "codeception/module-doctrine2": "^3.0", "doctrine/orm": "^2.10", + "symfony/browser-kit": "^4.4 | ^5.0 | ^6.0", + "symfony/cache": "^4.4 | ^5.0 | ^6.0", + "symfony/config": "^4.4 | ^5.0 | ^6.0", + "symfony/dependency-injection": "^4.4 | ^5.0 | ^6.0", + "symfony/dom-crawler": "^4.4 | ^5.0 | ^6.0", + "symfony/error-handler": "^4.4 | ^5.0 | ^6.0", + "symfony/filesystem": "^4.4 | ^5.0 | ^6.0", "symfony/form": "^4.4 | ^5.0 | ^6.0", "symfony/framework-bundle": "^4.4 | ^5.0 | ^6.0", + "symfony/http-foundation": "^4.4 | ^5.0 | ^6.0", "symfony/http-kernel": "^4.4 | ^5.0 | ^6.0", "symfony/mailer": "^4.4 | ^5.0 | ^6.0", + "symfony/mime": "^4.4 | ^5.0 | ^6.0", + "symfony/options-resolver": "^4.4 | ^5.0 | ^6.0", + "symfony/property-access": "^4.4 | ^5.0 | ^6.0", + "symfony/property-info": "^4.4 | ^5.0 | ^6.0", "symfony/routing": "^4.4 | ^5.0 | ^6.0", "symfony/security-bundle": "^4.4 | ^5.0 | ^6.0", + "symfony/security-core": "^4.4 | ^5.0 | ^6.0", + "symfony/security-csrf": "^4.4 | ^5.0 | ^6.0", + "symfony/security-http": "^4.4 | ^5.0 | ^6.0", "symfony/twig-bundle": "^4.4 | ^5.0 | ^6.0", + "symfony/var-exporter": "^4.4 | ^5.0 | ^6.0", "vlucas/phpdotenv": "^4.2 | ^5.4" }, "suggest": { From 5798e3e6328d20e9c41a5d1680f245e9881a4a43 Mon Sep 17 00:00:00 2001 From: Tavo Nieves J Date: Fri, 22 Dec 2023 23:02:09 -0500 Subject: [PATCH 8/8] EventsAssertionsTrait refactor --- .../Module/Symfony/EventsAssertionsTrait.php | 312 +++++++----------- 1 file changed, 115 insertions(+), 197 deletions(-) diff --git a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php index f6d56cc3..0ef1ee4c 100644 --- a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php @@ -5,44 +5,11 @@ namespace Codeception\Module\Symfony; use Symfony\Component\HttpKernel\DataCollector\EventDataCollector; -use Symfony\Component\VarDumper\Cloner\Data; - use function is_array; use function is_object; trait EventsAssertionsTrait { - /** - * Verifies that there were no orphan events during the test. - * - * An orphan event is an event that was triggered by manually executing the - * [`dispatch()`](https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event) method - * of the EventDispatcher but was not handled by any listener after it was dispatched. - * - * ```php - * dontSeeOrphanEvent(); - * $I->dontSeeOrphanEvent('App\MyEvent'); - * $I->dontSeeOrphanEvent(new App\Events\MyEvent()); - * $I->dontSeeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']); - * ``` - * - * @param object|string|string[] $expected - */ - public function dontSeeOrphanEvent(array|object|string $expected = null): void - { - $eventCollector = $this->grabEventCollector(__FUNCTION__); - - $data = $eventCollector->getOrphanedEvents(); - $expected = is_array($expected) ? $expected : [$expected]; - - if ($expected === null) { - $this->assertSame(0, $data->count()); - } else { - $this->assertEventTriggered($data, $expected, true); - } - } - /** * Verifies that there were no events during the test. * Both regular and orphan events are checked. @@ -51,29 +18,35 @@ public function dontSeeOrphanEvent(array|object|string $expected = null): void * dontSeeEvent(); * $I->dontSeeEvent('App\MyEvent'); - * $I->dontSeeEvent(new App\Events\MyEvent()); * $I->dontSeeEvent(['App\MyEvent', 'App\MyOtherEvent']); * ``` * - * @param array|object|string|null $expected + * @param string|string[]|null $expected */ - public function dontSeeEvent(array|object|string $expected = null): void + public function dontSeeEvent(array|string $expected = null): void { - $eventCollector = $this->grabEventCollector(__FUNCTION__); - - $data = [ - $eventCollector->getOrphanedEvents(), - $eventCollector->getCalledListeners(), - ]; - $expected = is_array($expected) ? $expected : [$expected]; + $actualEvents = array_merge(array_column($this->getCalledListeners(), 'event')); + $actual = [$this->getOrphanedEvents(), $actualEvents]; + $this->assertEventTriggered(false, $expected, $actual); + } - if ($expected === null) { - foreach ($data as $dataItem) { - $this->assertSame(0, $dataItem->count()); - } - } else { - $this->assertEventTriggered($data, $expected, true); - } + /** + * Verifies that one or more event listeners were not called during the test. + * + * ```php + * dontSeeEventListenerIsCalled('App\MyEventListener'); + * $I->dontSeeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']); + * $I->dontSeeEventListenerIsCalled('App\MyEventListener', 'my.event); + * $I->dontSeeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']); + * ``` + * + * @param class-string|class-string[] $expected + * @param string|string[] $events + */ + public function dontSeeEventListenerIsCalled(array|object|string $expected, array|string $events = []): void + { + $this->assertListenerCalled(false, $expected, $events); } /** @@ -99,39 +72,7 @@ public function dontSeeEventTriggered(array|object|string $expected): void } /** - * Verifies that one or more event listeners were not called during the test. - * - * ```php - * dontSeeEventListenerIsCalled('App\MyEventListener'); - * $I->dontSeeEventListenerIsCalled(new App\Events\MyEventListener()); - * $I->dontSeeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']); - * $I->dontSeeEventListenerIsCalled('App\MyEventListener', 'my.event); - * $I->dontSeeEventListenerIsCalled(new App\Events\MyEventListener(), new MyEvent()); - * $I->dontSeeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']); - * ``` - * - * @param object|string|string[] $expected - */ - public function dontSeeEventListenerIsCalled( - array|object|string $expected, - array|object|string $withEvents = [] - ): void { - $eventCollector = $this->grabEventCollector(__FUNCTION__); - - $data = $eventCollector->getCalledListeners(); - $expected = is_array($expected) ? $expected : [$expected]; - $withEvents = is_array($withEvents) ? $withEvents : [$withEvents]; - - if (!empty($withEvents) && count($expected) > 1) { - $this->fail('You cannot check for events when using multiple listeners. Make multiple assertions instead.'); - } - - $this->assertListenerCalled($data, $expected, $withEvents, true); - } - - /** - * Verifies that one or more orphan events were dispatched during the test. + * Verifies that there were no orphan events during the test. * * An orphan event is an event that was triggered by manually executing the * [`dispatch()`](https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event) method @@ -139,21 +80,17 @@ public function dontSeeEventListenerIsCalled( * * ```php * seeOrphanEvent('App\MyEvent'); - * $I->seeOrphanEvent(new App\Events\MyEvent()); - * $I->seeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']); + * $I->dontSeeOrphanEvent(); + * $I->dontSeeOrphanEvent('App\MyEvent'); + * $I->dontSeeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']); * ``` * - * @param object|string|string[] $expected + * @param string|string[] $expected */ - public function seeOrphanEvent(array|object|string $expected): void + public function dontSeeOrphanEvent(array|string $expected = null): void { - $eventCollector = $this->grabEventCollector(__FUNCTION__); - - $data = $eventCollector->getOrphanedEvents(); - $expected = is_array($expected) ? $expected : [$expected]; - - $this->assertEventTriggered($data, $expected); + $actual = [$this->getOrphanedEvents()]; + $this->assertEventTriggered(false, $expected, $actual); } /** @@ -166,23 +103,35 @@ public function seeOrphanEvent(array|object|string $expected): void * ```php * seeEvent('App\MyEvent'); - * $I->seeEvent(new App\Events\MyEvent()); * $I->seeEvent(['App\MyEvent', 'App\MyOtherEvent']); * ``` * - * @param array|object|string $expected + * @param string|string[] $expected */ - public function seeEvent(array|object|string $expected): void + public function seeEvent(array|string $expected): void { - $eventCollector = $this->grabEventCollector(__FUNCTION__); - - $data = [ - $eventCollector->getOrphanedEvents(), - $eventCollector->getCalledListeners(), - ]; - $expected = is_array($expected) ? $expected : [$expected]; + $actualEvents = array_merge(array_column($this->getCalledListeners(), 'event')); + $actual = [$this->getOrphanedEvents(), $actualEvents]; + $this->assertEventTriggered(true, $expected, $actual); + } - $this->assertEventTriggered($data, $expected); + /** + * Verifies that one or more event listeners were called during the test. + * + * ```php + * seeEventListenerIsCalled('App\MyEventListener'); + * $I->seeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']); + * $I->seeEventListenerIsCalled('App\MyEventListener', 'my.event); + * $I->seeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']); + * ``` + * + * @param class-string|class-string[] $expected + * @param string|string[] $events + */ + public function seeEventListenerIsCalled(array|object|string $expected, array|string $events = []): void + { + $this->assertListenerCalled(true, $expected, $events); } /** @@ -208,138 +157,107 @@ public function seeEventTriggered(array|object|string $expected): void } /** - * Verifies that one or more event listeners were called during the test. + * Verifies that one or more orphan events were dispatched during the test. + * + * An orphan event is an event that was triggered by manually executing the + * [`dispatch()`](https://symfony.com/doc/current/components/event_dispatcher.html#dispatch-the-event) method + * of the EventDispatcher but was not handled by any listener after it was dispatched. * * ```php * seeEventListenerIsCalled('App\MyEventListener'); - * $I->seeEventListenerIsCalled(new App\Events\MyEventListener()); - * $I->seeEventListenerIsCalled(['App\MyEventListener', 'App\MyOtherEventListener']); - * $I->seeEventListenerIsCalled('App\MyEventListener', 'my.event); - * $I->seeEventListenerIsCalled(new App\Events\MyEventListener(), new MyEvent()); - * $I->seeEventListenerIsCalled('App\MyEventListener', ['my.event', 'my.other.event']); + * $I->seeOrphanEvent('App\MyEvent'); + * $I->seeOrphanEvent(['App\MyEvent', 'App\MyOtherEvent']); * ``` * - * @param object|string|string[] $expected + * @param string|string[] $expected */ - public function seeEventListenerIsCalled( - array|object|string $expected, - array|object|string $withEvents = [] - ): void { - $eventCollector = $this->grabEventCollector(__FUNCTION__); - - $data = $eventCollector->getCalledListeners(); - $expected = is_array($expected) ? $expected : [$expected]; - $withEvents = is_array($withEvents) ? $withEvents : [$withEvents]; + public function seeOrphanEvent(array|string $expected): void + { + $actual = [$this->getOrphanedEvents()]; + $this->assertEventTriggered(true, $expected, $actual); + } - if (!empty($withEvents) && count($expected) > 1) { - $this->fail('You cannot check for events when using multiple listeners. Make multiple assertions instead.'); - } + protected function getCalledListeners(): array + { + $eventCollector = $this->grabEventCollector(__FUNCTION__); + $calledListeners = $eventCollector->getCalledListeners($this->getDefaultDispatcher()); + return [...$calledListeners->getValue(true)]; + } - $this->assertListenerCalled($data, $expected, $withEvents); + protected function getOrphanedEvents(): array + { + $eventCollector = $this->grabEventCollector(__FUNCTION__); + $orphanedEvents = $eventCollector->getOrphanedEvents($this->getDefaultDispatcher()); + return [...$orphanedEvents->getValue(true)]; } - protected function assertEventTriggered( - array|Data $data, - array $expected, - bool $invertAssertion = false - ): void { - $assertTrue = !$invertAssertion; - $data = is_array($data) ? $data : [$data]; - $totalEvents = array_sum(array_map('count', $data)); + protected function assertEventTriggered(bool $assertTrue, array|object|string|null $expected, array $actual): void + { + $actualEvents = array_merge(...$actual); - if ($assertTrue && $totalEvents === 0) { - $this->fail('No event was triggered'); + if ($assertTrue) $this->assertNotEmpty($actualEvents, 'No event was triggered'); + if ($expected === null) { + $this->assertEmpty($actualEvents); + return; } - $actualEventsCollection = array_map(static fn (Data $data) => $data->getValue(true), $data); - - foreach ($expected as $expectedEvent) { + $expected = is_object($expected) ? $expected::class : $expected; + foreach ((array)$expected as $expectedEvent) { $expectedEvent = is_object($expectedEvent) ? $expectedEvent::class : $expectedEvent; + $eventTriggered = in_array($expectedEvent, $actualEvents); + $message = $assertTrue ? "The '{$expectedEvent}' event did not trigger" : "The '{$expectedEvent}' event triggered"; - - $eventTriggered = false; - - foreach ($actualEventsCollection as $actualEvents) { - $eventTriggered = $eventTriggered || $this->eventWasTriggered($actualEvents, (string)$expectedEvent); - } - - if ($assertTrue) { - $this->assertTrue($eventTriggered, $message); - } else { - $this->assertFalse($eventTriggered, $message); - } + $this->assertSame($assertTrue, $eventTriggered, $message); } } - protected function assertListenerCalled( - Data $data, - array $expectedListeners, - array $withEvents, - bool $invertAssertion = false - ): void { - $assertTrue = !$invertAssertion; + protected function assertListenerCalled(bool $assertTrue, array|object|string $expectedListeners, array|object|string $expectedEvents): void + { + $expectedListeners = is_array($expectedListeners) ? $expectedListeners : [$expectedListeners]; + $expectedEvents = is_array($expectedEvents) ? $expectedEvents : [$expectedEvents]; - if ($assertTrue && $data->count() === 0) { - $this->fail('No event listener was called'); + if (empty($expectedEvents)) { + $expectedEvents = [null]; + } elseif (count($expectedListeners) > 1) { + $this->fail('You cannot check for events when using multiple listeners. Make multiple assertions instead.'); } - $actual = $data->getValue(true); - $expectedEvents = empty($withEvents) ? [null] : $withEvents; + $actualEvents = $this->getCalledListeners(); + if ($assertTrue && empty($actualEvents)) { + $this->fail('No event listener was called'); + } foreach ($expectedListeners as $expectedListener) { $expectedListener = is_object($expectedListener) ? $expectedListener::class : $expectedListener; foreach ($expectedEvents as $expectedEvent) { + $listenerCalled = $this->listenerWasCalled($expectedListener, $expectedEvent, $actualEvents); $message = "The '{$expectedListener}' listener was called" . ($expectedEvent ? " for the '{$expectedEvent}' event" : ''); - - $condition = $this->listenerWasCalled($actual, $expectedListener, $expectedEvent); - - if ($assertTrue) { - $this->assertTrue($condition, $message); - } else { - $this->assertFalse($condition, $message); - } + $this->assertSame($assertTrue, $listenerCalled, $message); } } } - protected function eventWasTriggered(array $actual, string $expectedEvent): bool + private function listenerWasCalled(string $expectedListener, ?string $expectedEvent, array $actualEvents): bool { - $triggered = false; - - foreach ($actual as $actualEvent) { - if (is_array($actualEvent)) { // Called Listeners - if ($actualEvent['event'] === $expectedEvent) { - $triggered = true; - } - } elseif ($actualEvent === $expectedEvent) { // Orphan Events - $triggered = true; + foreach ($actualEvents as $actualEvent) { + if ( + isset($actualEvent['pretty'], $actualEvent['event']) + && str_starts_with($actualEvent['pretty'], $expectedListener) + && ($expectedEvent === null || $actualEvent['event'] === $expectedEvent) + ) { + return true; } } - - return $triggered; + return false; } - protected function listenerWasCalled(array $actual, string $expectedListener, string|null $expectedEvent): bool + protected function getDefaultDispatcher(): string { - $called = false; - - foreach ($actual as $actualEvent) { - // Called Listeners - if (is_array($actualEvent) && str_starts_with($actualEvent['pretty'], $expectedListener)) { - if ($expectedEvent === null) { - $called = true; - } elseif ($actualEvent['event'] === $expectedEvent) { - $called = true; - } - } - } - - return $called; + return 'event_dispatcher'; } protected function grabEventCollector(string $function): EventDataCollector pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy