From 8bae088ceefab1725f7c3c5b1825acfe71f21e1a Mon Sep 17 00:00:00 2001 From: Aaron Gustavo Nieves Date: Sun, 21 Jun 2026 14:46:07 -0500 Subject: [PATCH] Improve assertion traits: DX, type safety, and performance --- .github/workflows/main.yml | 3 +- composer.json | 2 +- src/Codeception/Module/Symfony.php | 18 +++++++---- .../Module/Symfony/BrowserAssertionsTrait.php | 17 ++++++----- src/Codeception/Module/Symfony/CacheTrait.php | 29 ++++++++++-------- .../Module/Symfony/ConsoleAssertionsTrait.php | 1 + .../Symfony/DomCrawlerAssertionsTrait.php | 11 +++++-- .../Module/Symfony/EventsAssertionsTrait.php | 30 ++++++++++++------- .../Module/Symfony/FormAssertionsTrait.php | 8 ++--- .../Symfony/HttpClientAssertionsTrait.php | 25 +++++++++------- .../Symfony/HttpKernelAssertionsTrait.php | 6 ++-- .../Module/Symfony/LoggerAssertionsTrait.php | 8 ++--- .../Module/Symfony/MailerAssertionsTrait.php | 4 +-- .../Symfony/NotifierAssertionsTrait.php | 12 ++++++-- .../Module/Symfony/RouterAssertionsTrait.php | 9 +++--- .../Module/Symfony/SessionAssertionsTrait.php | 1 + .../Module/Symfony/TimeAssertionsTrait.php | 4 +-- .../Symfony/TranslationAssertionsTrait.php | 4 +-- .../Module/Symfony/TwigAssertionsTrait.php | 4 +-- .../Symfony/ValidatorAssertionsTrait.php | 25 ++++++++-------- 20 files changed, 133 insertions(+), 88 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b94da07..cf9dfe28 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,8 +55,9 @@ jobs: if [ "${{ matrix.symfony }}" = "7.4" ]; then composer require codeception/module-rest --dev + git -C framework-tests checkout -- composer.json git -C framework-tests apply resetFormatsAfterRequest_issue_test.patch - composer -d framework-tests install --no-progress + composer -d framework-tests update --no-progress php framework-tests/bin/console lexik:jwt:generate-keypair --skip-if-exists php vendor/bin/codecept run Functional -c framework-tests fi \ No newline at end of file diff --git a/composer.json b/composer.json index ea47ec8d..3c051e63 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "codeception/module-doctrine": "^3.3", "doctrine/orm": "^3.6", "friendsofphp/php-cs-fixer": "^3.94", - "phpstan/phpstan": "^2.1", + "phpstan/phpstan": "^2.2", "phpunit/phpunit": "^11.0 | ^12.0", "symfony/browser-kit": "^5.4 | ^6.4 | ^7.4 | ^8.0", "symfony/cache": "^5.4 | ^6.4 | ^7.4 | ^8.0", diff --git a/src/Codeception/Module/Symfony.php b/src/Codeception/Module/Symfony.php index ba8ece35..a130e0ee 100644 --- a/src/Codeception/Module/Symfony.php +++ b/src/Codeception/Module/Symfony.php @@ -49,8 +49,6 @@ use Symfony\Component\Notifier\DataCollector\NotificationDataCollector; use Symfony\Component\VarDumper\Cloner\Data; -use function array_filter; -use function array_map; use function class_exists; use function codecept_root_dir; use function count; @@ -61,6 +59,7 @@ use function ini_get; use function ini_set; use function is_object; +use function is_scalar; use function is_subclass_of; use function sprintf; @@ -334,8 +333,11 @@ protected function getKernelClass(): string if (file_exists($expectedKernelPath)) { include_once $expectedKernelPath; } else { - foreach (glob($path . DIRECTORY_SEPARATOR . '*Kernel.php') ?: [] as $file) { - include_once $file; + $kernelFiles = glob($path . DIRECTORY_SEPARATOR . '*Kernel.php', GLOB_NOSORT); + if ($kernelFiles !== false) { + foreach ($kernelFiles as $file) { + include_once $file; + } } } @@ -445,7 +447,13 @@ private function debugSecurityData(SecurityDataCollector $securityCollector): vo $roles = $roles->getValue(true); } - $rolesStr = implode(',', array_map('strval', array_filter((array) $roles, 'is_scalar'))); + $scalarRoles = []; + foreach ((array) $roles as $role) { + if (is_scalar($role)) { + $scalarRoles[] = (string) $role; + } + } + $rolesStr = implode(',', $scalarRoles); $this->debugSection('User', sprintf('%s [%s]', $securityCollector->getUser(), $rolesStr)); } diff --git a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php index e86a7dda..b772907f 100644 --- a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php @@ -109,7 +109,8 @@ public function assertResponseCookieValueSame(string $name, string $expectedValu */ public function assertResponseFormatSame(?string $expectedFormat, string $message = ''): void { - $this->assertThatForResponse(new ResponseFormatSame($this->getClient()->getRequest(), $expectedFormat), $message); + $client = $this->getClient(); + $this->assertThatForResponse(new ResponseFormatSame($client->getRequest(), $expectedFormat), $message); } /** @@ -233,14 +234,14 @@ public function assertResponseRedirects(?string $expectedLocation = null, ?int $ { $this->assertThatForResponse(new ResponseIsRedirected($verbose), $message); - if ($expectedLocation) { + if ($expectedLocation !== null) { $constraint = class_exists(ResponseHeaderLocationSame::class) ? new ResponseHeaderLocationSame($this->getClient()->getRequest(), $expectedLocation) : new ResponseHeaderSame('Location', $expectedLocation); $this->assertThatForResponse($constraint, $message); } - if ($expectedCode) { + if ($expectedCode !== null) { $this->assertThatForResponse(new ResponseStatusCodeSame($expectedCode), $message); } } @@ -317,8 +318,9 @@ protected function doRebootClientKernel(): void {} public function seePageIsAvailable(?string $url = null): void { if ($url !== null) { - $this->getClient()->request('GET', $url); - $this->assertStringContainsString($url, $this->getClient()->getRequest()->getRequestUri()); + $client = $this->getClient(); + $client->request('GET', $url); + $this->assertStringContainsString($url, $client->getRequest()->getRequestUri()); } $this->assertResponseIsSuccessful(); @@ -370,10 +372,11 @@ public function submitSymfonyForm(string $name, array $fields): void $params[$name . $key] = $value; } - $node = $this->getClient()->getCrawler()->filter($selector); + $client = $this->getClient(); + $node = $client->getCrawler()->filter($selector); $this->assertGreaterThan(0, $node->count(), sprintf('Form "%s" not found.', $selector)); $form = $node->form(); - $this->getClient()->submit($form, $params); + $client->submit($form, $params); } protected function assertThatForClient(Constraint $constraint, string $message = ''): void diff --git a/src/Codeception/Module/Symfony/CacheTrait.php b/src/Codeception/Module/Symfony/CacheTrait.php index 1b47b413..1c7940fb 100644 --- a/src/Codeception/Module/Symfony/CacheTrait.php +++ b/src/Codeception/Module/Symfony/CacheTrait.php @@ -7,8 +7,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\Profiler\Profile; +use function array_key_exists; use function array_unique; use function array_values; +use function is_string; trait CacheTrait { @@ -40,11 +42,13 @@ protected function getInternalDomains(): array $domains = []; foreach ($this->grabRouterService()->getRouteCollection() as $route) { - if ($route->getHost() !== '') { - $regex = $route->compile()->getHostRegex(); - if ($regex !== null && $regex !== '') { - $domains[] = $regex; - } + if ($route->getHost() === '') { + continue; + } + + $hostRegex = $route->compile()->getHostRegex(); + if ($hostRegex !== null && $hostRegex !== '') { + $domains[] = $hostRegex; } } @@ -67,22 +71,23 @@ protected function clearRouterCache(): void */ protected function grabCachedService(string $expectedClass, array $serviceIds): ?object { - $serviceId = $this->state[$expectedClass] ??= (function () use ($serviceIds, $expectedClass): ?string { + if (!array_key_exists($expectedClass, $this->state)) { + $this->state[$expectedClass] = null; foreach ($serviceIds as $id) { - if ($this->getService($id) instanceof $expectedClass) { - return $id; + $service = $this->getService($id); + if ($service instanceof $expectedClass) { + $this->state[$expectedClass] = $id; + break; } } + } - return null; - })(); - + $serviceId = $this->state[$expectedClass]; if (!is_string($serviceId)) { return null; } $service = $this->getService($serviceId); - return $service instanceof $expectedClass ? $service : null; } } diff --git a/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php b/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php index 09da7769..9a2def50 100644 --- a/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php @@ -60,6 +60,7 @@ public function runSymfonyConsoleCommand( */ private function configureOptions(array $parameters): array { + /** @var array $options */ $options = []; foreach ($parameters as $key => $value) { diff --git a/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php b/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php index 8a03bfc7..2c4fa177 100644 --- a/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php @@ -11,6 +11,8 @@ use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorTextContains; use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorTextSame; +use function sprintf; + trait DomCrawlerAssertionsTrait { /** @@ -172,11 +174,14 @@ private function assertCheckboxState(string $fieldName, bool $checked, string $m private function assertInputValue(string $fieldName, string $expectedValue, bool $same, string $message): void { - $this->assertThatCrawler(new CrawlerSelectorExists("input[name=\"$fieldName\"]"), $message); - $constraint = new CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $expectedValue); + $selector = "input[name=\"$fieldName\"]"; + $context = $message ?: sprintf('Value of input "%s"', $fieldName); + $this->assertThatCrawler(new CrawlerSelectorExists($selector), $context); + + $constraint = new CrawlerSelectorAttributeValueSame($selector, 'value', $expectedValue); if (!$same) { $constraint = new LogicalNot($constraint); } - $this->assertThatCrawler($constraint, $message); + $this->assertThatCrawler($constraint, $context); } } diff --git a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php index 3dc517fb..d6ccd993 100644 --- a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php @@ -249,6 +249,14 @@ protected function assertListenerCalled( Assert::fail('No event listener was called.'); } + $listenersByEvent = []; + $allEventListeners = []; + foreach ($actualEvents as $actualEvent) { + $pretty = $actualEvent['pretty']; + $allEventListeners[] = $pretty; + $listenersByEvent[$actualEvent['event']][] = $pretty; + } + foreach ($expectedListeners as $listener) { $listenerName = match (true) { is_array($listener) && isset($listener[0]) => is_string($listener[0]) ? $listener[0] : (is_object($listener[0]) ? $listener[0]::class : 'array'), @@ -259,26 +267,28 @@ protected function assertListenerCalled( foreach ($expectedEvents as $event) { $eventStr = (string) $event; + $wasCalled = $event === null + ? $this->hasListenerPrefix($allEventListeners, $listenerName) + : $this->hasListenerPrefix($listenersByEvent[$event] ?? [], $listenerName); + $this->assertSame( $shouldBeCalled, - $this->listenerWasCalled($listenerName, $event, $actualEvents), + $wasCalled, sprintf("The '%s' listener was %scalled%s", $listenerName, $shouldBeCalled ? 'not ' : '', $event ? " for the '{$eventStr}' event" : '') ); } } } - /** @param list $actualEvents */ - private function listenerWasCalled(string $expectedListener, ?string $expectedEvent, array $actualEvents): bool + /** @param list $listeners */ + private function hasListenerPrefix(array $listeners, string $listenerName): bool { - foreach ($actualEvents as $actualEvent) { - if ($expectedEvent !== null && $actualEvent['event'] !== $expectedEvent) { - continue; - } - if (str_starts_with($actualEvent['pretty'], $expectedListener)) { + foreach ($listeners as $actualListener) { + if (str_starts_with($actualListener, $listenerName)) { return true; } } + return false; } @@ -287,8 +297,8 @@ protected function getDefaultDispatcher(): string return 'event_dispatcher'; } - protected function grabEventCollector(string $function): EventDataCollector + protected function grabEventCollector(string $callingFunction): EventDataCollector { - return $this->grabCollector(DataCollectorName::EVENTS, $function); + return $this->grabCollector(DataCollectorName::EVENTS, $callingFunction); } } diff --git a/src/Codeception/Module/Symfony/FormAssertionsTrait.php b/src/Codeception/Module/Symfony/FormAssertionsTrait.php index 59bf121c..671fd98b 100644 --- a/src/Codeception/Module/Symfony/FormAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/FormAssertionsTrait.php @@ -175,14 +175,14 @@ public function seeFormHasErrors(): void $this->assertGreaterThan(0, $this->getFormErrorsCount(__FUNCTION__), 'Expecting that the form has errors, but there were none!'); } - protected function grabFormCollector(string $function): FormDataCollector + protected function grabFormCollector(string $callingFunction): FormDataCollector { - return $this->grabCollector(DataCollectorName::FORM, $function); + return $this->grabCollector(DataCollectorName::FORM, $callingFunction); } - private function getFormErrorsCount(string $function): int + private function getFormErrorsCount(string $callingFunction): int { - $collector = $this->grabFormCollector($function); + $collector = $this->grabFormCollector($callingFunction); $rawData = $this->getRawCollectorData($collector); return isset($rawData['nb_errors']) && is_numeric($rawData['nb_errors']) ? (int) $rawData['nb_errors'] : 0; diff --git a/src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php b/src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php index 89b19118..517a33fb 100644 --- a/src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php @@ -90,16 +90,17 @@ public function assertNotHttpClientRequest( */ private function hasHttpClientRequest( string $httpClientId, - string $function, + string $callingFunction, string $expectedUrl, string $expectedMethod, string|array|null $expectedBody = null, array $expectedHeaders = [] ): bool { $expectedHeadersLower = $expectedHeaders === [] ? [] : array_change_key_case($expectedHeaders); + $traces = $this->getHttpClientTraces($httpClientId, $callingFunction); - foreach ($this->getHttpClientTraces($httpClientId, $function) as $trace) { - if (!is_array($trace) || ($trace['method'] ?? null) !== $expectedMethod) { + foreach ($traces as $trace) { + if (($trace['method'] ?? null) !== $expectedMethod) { continue; } @@ -124,7 +125,11 @@ private function hasHttpClientRequest( } $actualHeaders = $this->extractValue($options['headers'] ?? []); - if (is_array($actualHeaders) && $expectedHeadersLower === array_intersect_key(array_change_key_case($actualHeaders), $expectedHeadersLower)) { + if (!is_array($actualHeaders)) { + continue; + } + + if ($expectedHeadersLower === array_intersect_key(array_change_key_case($actualHeaders), $expectedHeadersLower)) { return true; } } @@ -132,15 +137,15 @@ private function hasHttpClientRequest( return false; } - /** @return array */ - private function getHttpClientTraces(string $httpClientId, string $function): array + /** @return list> */ + private function getHttpClientTraces(string $httpClientId, string $callingFunction): array { - $clients = $this->grabHttpClientCollector($function)->getClients(); + $clients = $this->grabHttpClientCollector($callingFunction)->getClients(); if (!isset($clients[$httpClientId]) || !is_array($clients[$httpClientId])) { Assert::fail(sprintf('HttpClient "%s" is not registered.', $httpClientId)); } - /** @var array{traces: array} $clientData */ + /** @var array{traces: list>} $clientData */ $clientData = $clients[$httpClientId]; return $clientData['traces']; } @@ -155,8 +160,8 @@ private function extractValue(mixed $traceData): mixed }; } - protected function grabHttpClientCollector(string $function): HttpClientDataCollector + protected function grabHttpClientCollector(string $callingFunction): HttpClientDataCollector { - return $this->grabCollector(DataCollectorName::HTTP_CLIENT, $function); + return $this->grabCollector(DataCollectorName::HTTP_CLIENT, $callingFunction); } } diff --git a/src/Codeception/Module/Symfony/HttpKernelAssertionsTrait.php b/src/Codeception/Module/Symfony/HttpKernelAssertionsTrait.php index 6a025f7c..19094ad8 100644 --- a/src/Codeception/Module/Symfony/HttpKernelAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/HttpKernelAssertionsTrait.php @@ -45,16 +45,16 @@ abstract protected function getProfile(): ?Profile; * ))))))))) * ) */ - protected function grabCollector(DataCollectorName $name, string $function = '', ?string $message = null): DataCollectorInterface + protected function grabCollector(DataCollectorName $name, string $callingFunction = '', ?string $message = null): DataCollectorInterface { $profile = $this->getProfile(); if ($profile === null) { - Assert::fail(sprintf("The Profile is needed to use the '%s' function.", $function)); + Assert::fail(sprintf("The Profile is needed to use the '%s' function.", $callingFunction)); } if (!$profile->hasCollector($name->value)) { - Assert::fail($message ?: sprintf("The '%s' collector is needed to use the '%s' function.", $name->value, $function)); + Assert::fail($message ?: sprintf("The '%s' collector is needed to use the '%s' function.", $name->value, $callingFunction)); } return $profile->getCollector($name->value); diff --git a/src/Codeception/Module/Symfony/LoggerAssertionsTrait.php b/src/Codeception/Module/Symfony/LoggerAssertionsTrait.php index 84465b1a..4e9f2b0b 100644 --- a/src/Codeception/Module/Symfony/LoggerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/LoggerAssertionsTrait.php @@ -35,7 +35,7 @@ public function dontSeeDeprecations(string $message = ''): void /** @var array $log */ foreach ($logs as $log) { - if (!isset($log['type']) || $log['type'] !== 'deprecation') { + if (($log['type'] ?? null) !== 'deprecation') { continue; } $msg = $log['message']; @@ -48,7 +48,7 @@ public function dontSeeDeprecations(string $message = ''): void $foundDeprecations[] = (string) $msg; } $count = count($foundDeprecations); - $errorMessage = $message ?: sprintf( + $errorMessage = $message !== '' ? $message : sprintf( "Found %d deprecation message%s in the log:\n%s", $count, $count !== 1 ? 's' : '', @@ -57,8 +57,8 @@ public function dontSeeDeprecations(string $message = ''): void $this->assertEmpty($foundDeprecations, $errorMessage); } - protected function grabLoggerCollector(string $function): LoggerDataCollector + protected function grabLoggerCollector(string $callingFunction): LoggerDataCollector { - return $this->grabCollector(DataCollectorName::LOGGER, $function); + return $this->grabCollector(DataCollectorName::LOGGER, $callingFunction); } } diff --git a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php index 00d8aa22..bcfccfd7 100644 --- a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php @@ -160,9 +160,9 @@ public function getMailerEvent(int $index = 0, ?string $transport = null): ?Mess protected function grabLastSentRawMessage(): ?RawMessage { - $messages = $this->getMessageMailerEvents()->getMessages(); + $events = $this->getMessageMailerEvents()->getEvents(); - return $messages ? $messages[array_key_last($messages)] : null; + return $events ? $events[array_key_last($events)]->getMessage() : null; } protected function getMessageMailerEvents(): MessageEvents diff --git a/src/Codeception/Module/Symfony/NotifierAssertionsTrait.php b/src/Codeception/Module/Symfony/NotifierAssertionsTrait.php index 0577ddf7..c833bec6 100644 --- a/src/Codeception/Module/Symfony/NotifierAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/NotifierAssertionsTrait.php @@ -163,9 +163,13 @@ public function dontSeeNotificationIsSent(): void */ public function grabLastSentNotification(): ?MessageInterface { - $notifications = $this->getNotifierMessages(); + $events = $this->getNotifierEvents(); - return $notifications ? $notifications[array_key_last($notifications)] : null; + if ($events === []) { + return null; + } + + return $events[array_key_last($events)]->getMessage(); } /** @@ -238,7 +242,9 @@ protected function getNotifierMessages(?string $transportName = null): array */ public function getNotifierMessage(int $index = 0, ?string $transportName = null): ?MessageInterface { - return $this->getNotifierMessages($transportName)[$index] ?? null; + $event = $this->getNotifierEvents($transportName)[$index] ?? null; + + return $event?->getMessage(); } protected function getNotificationEvents(): NotificationEvents diff --git a/src/Codeception/Module/Symfony/RouterAssertionsTrait.php b/src/Codeception/Module/Symfony/RouterAssertionsTrait.php index 3550301d..245ee445 100644 --- a/src/Codeception/Module/Symfony/RouterAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/RouterAssertionsTrait.php @@ -5,6 +5,7 @@ namespace Codeception\Module\Symfony; use PHPUnit\Framework\Assert; +use Stringable; use Symfony\Component\Routing\RouterInterface; use function array_intersect_key; @@ -25,7 +26,7 @@ trait RouterAssertionsTrait * $I->amOnAction('ArticleController', ['slug' => 'lorem-ipsum']); * ``` * - * @param array $params + * @param array|null> $params */ public function amOnAction(string $action, array $params = []): void { @@ -41,7 +42,7 @@ public function amOnAction(string $action, array $params = []): void * $I->amOnRoute('posts.show', ['id' => 34]); * ``` * - * @param array $params + * @param array|null> $params */ public function amOnRoute(string $routeName, array $params = []): void { @@ -85,7 +86,7 @@ public function seeCurrentActionIs(string $action): void * $I->seeCurrentRouteIs('posts.show', ['id' => 8]); * ``` * - * @param array $params + * @param array|null> $params */ public function seeCurrentRouteIs(string $routeName, array $params = []): void { @@ -157,7 +158,7 @@ private function assertRouteExists(string $routeName): void ); } - /** @param array $params */ + /** @param array|null> $params */ private function openRoute(string $routeName, array $params = []): void { $this->getClient()->request('GET', $this->grabRouterService()->generate($routeName, $params)); diff --git a/src/Codeception/Module/Symfony/SessionAssertionsTrait.php b/src/Codeception/Module/Symfony/SessionAssertionsTrait.php index 57d42b4c..431e72a5 100644 --- a/src/Codeception/Module/Symfony/SessionAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/SessionAssertionsTrait.php @@ -123,6 +123,7 @@ public function logoutProgrammatically(): void $session->invalidate(); $cookieJar = $this->getClient()->getCookieJar(); + foreach ($cookieJar->all() as $cookie) { $cookieName = $cookie->getName(); if ($cookieName === 'MOCKSESSID' || $cookieName === 'REMEMBERME' || $cookieName === $sessionName) { diff --git a/src/Codeception/Module/Symfony/TimeAssertionsTrait.php b/src/Codeception/Module/Symfony/TimeAssertionsTrait.php index 982f7fe0..c1e5a77f 100644 --- a/src/Codeception/Module/Symfony/TimeAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TimeAssertionsTrait.php @@ -44,8 +44,8 @@ public function seeRequestTimeIsLessThan(int|float $expectedMilliseconds): void ); } - protected function grabTimeCollector(string $function): TimeDataCollector + protected function grabTimeCollector(string $callingFunction): TimeDataCollector { - return $this->grabCollector(DataCollectorName::TIME, $function); + return $this->grabCollector(DataCollectorName::TIME, $callingFunction); } } diff --git a/src/Codeception/Module/Symfony/TranslationAssertionsTrait.php b/src/Codeception/Module/Symfony/TranslationAssertionsTrait.php index 0315b092..0f63d6df 100644 --- a/src/Codeception/Module/Symfony/TranslationAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TranslationAssertionsTrait.php @@ -171,8 +171,8 @@ public function seeMissingTranslationsCountLessThan(int $limit): void ); } - protected function grabTranslationCollector(string $function): TranslationDataCollector + protected function grabTranslationCollector(string $callingFunction): TranslationDataCollector { - return $this->grabCollector(DataCollectorName::TRANSLATION, $function); + return $this->grabCollector(DataCollectorName::TRANSLATION, $callingFunction); } } diff --git a/src/Codeception/Module/Symfony/TwigAssertionsTrait.php b/src/Codeception/Module/Symfony/TwigAssertionsTrait.php index 021e6456..6a928e50 100644 --- a/src/Codeception/Module/Symfony/TwigAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TwigAssertionsTrait.php @@ -76,8 +76,8 @@ public function seeRenderedTemplate(string $template): void ); } - protected function grabTwigCollector(string $function): TwigDataCollector + protected function grabTwigCollector(string $callingFunction): TwigDataCollector { - return $this->grabCollector(DataCollectorName::TWIG, $function); + return $this->grabCollector(DataCollectorName::TWIG, $callingFunction); } } diff --git a/src/Codeception/Module/Symfony/ValidatorAssertionsTrait.php b/src/Codeception/Module/Symfony/ValidatorAssertionsTrait.php index e516a5f1..2cedd59a 100644 --- a/src/Codeception/Module/Symfony/ValidatorAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ValidatorAssertionsTrait.php @@ -7,6 +7,7 @@ use Symfony\Component\Validator\ConstraintViolationInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; +use function array_filter; use function iterator_to_array; use function str_contains; @@ -87,23 +88,21 @@ public function seeViolatedConstraintMessage(string $expected, object $subject, protected function getViolationsForSubject(object $subject, ?string $propertyPath = null, ?string $constraint = null): array { $validator = $this->getValidatorService(); - $violations = $propertyPath ? $validator->validateProperty($subject, $propertyPath) : $validator->validate($subject); + $violations = $propertyPath !== null ? $validator->validateProperty($subject, $propertyPath) : $validator->validate($subject); - /** @var ConstraintViolationInterface[] $violations */ - $violations = iterator_to_array($violations); + $violations = iterator_to_array($violations, false); - if ($constraint !== null) { - $filteredViolations = []; - foreach ($violations as $violation) { - $violationConstraint = $violation->getConstraint(); - if ($violationConstraint !== null && $violationConstraint::class === $constraint) { - $filteredViolations[] = $violation; - } - } - return $filteredViolations; + if ($constraint === null) { + return $violations; } - return $violations; + return array_filter( + $violations, + static function (ConstraintViolationInterface $violation) use ($constraint): bool { + $c = $violation->getConstraint(); + return $c !== null && $c::class === $constraint; + } + ); } protected function getValidatorService(): ValidatorInterface