diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 885dd616..1898a021 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,19 +8,12 @@ jobs: strategy: matrix: - php: [8.0, 8.1, 8.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.*" + php: [8.1, 8.2, 8.3] + symfony: ["5.4.*", "6.4.*"] steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -30,67 +23,35 @@ jobs: extensions: ctype, iconv, intl, json, mbstring, pdo, pdo_sqlite coverage: none - - name: Checkout Symfony 4.4 Sample - if: "matrix.symfony == '4.4.*'" - uses: actions/checkout@v2 - with: - repository: Codeception/symfony-module-tests - path: framework-tests - ref: "4.4_codecept5" - - name: Checkout Symfony 5.4 Sample if: "matrix.symfony == '5.4.*'" - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: Codeception/symfony-module-tests path: framework-tests ref: "5.4_codecept5" - - name: Checkout Symfony 6.0 Sample - if: "matrix.symfony == '6.0.*'" - uses: actions/checkout@v2 + - name: Checkout Symfony 6.4 Sample + if: "matrix.symfony == '6.4.*'" + uses: actions/checkout@v4 with: repository: Codeception/symfony-module-tests path: framework-tests - ref: "6.0" - - - name: Checkout Symfony 6.1 Sample - if: "matrix.symfony == '6.1.*'" - uses: actions/checkout@v2 - with: - repository: Codeception/symfony-module-tests - path: framework-tests - ref: "6.1" - - - name: Checkout Symfony 6.2 Sample - if: "matrix.symfony == '6.2.*'" - uses: actions/checkout@v2 - with: - repository: Codeception/symfony-module-tests - 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" + ref: "6.4" - name: Get composer cache directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" - name: Cache composer dependencies - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} 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.*'" + - name: Install PHPUnit 9 for Symfony 5.4 + if: "matrix.symfony == '5.4.*'" run: composer require --dev --no-update "phpunit/phpunit=^9.0" - name: Install dependencies @@ -111,8 +72,8 @@ jobs: run: composer validate 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.*' || matrix.symfony == '6.3.*'" + - name: Install PHPUnit 10 in framework-tests for Symfony 6.4 + if: "matrix.symfony == '6.4.*'" run: composer require --dev --no-update "phpunit/phpunit=^10.0" working-directory: framework-tests diff --git a/LICENSE b/LICENSE index 61d82091..624026b5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2011-2020 Michael Bodnarchuk and contributors +Copyright (c) 2011-2024 Michael Bodnarchuk and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/composer.json b/composer.json index 24d5822d..52a4293b 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ ], "homepage": "https://codeception.com/", "require": { - "php": "^8.0", + "php": "^8.1", "ext-json": "*", "codeception/codeception": "^5.0.8", "codeception/lib-innerbrowser": "^3.1.1 | ^4.0" diff --git a/readme.md b/readme.md index c832d43a..49c9e0ab 100644 --- a/readme.md +++ b/readme.md @@ -9,8 +9,8 @@ A Codeception module for Symfony framework. ## Requirements -* `Symfony` `4.4.x`, `5.4.x`, `6.x` or higher, as per the [Symfony supported versions](https://symfony.com/releases). -* `PHP 8.0` or higher. +* `Symfony` `5.4.x`, `6.4.x` or higher, as per the [Symfony supported versions](https://symfony.com/releases). +* `PHP 8.1` or higher. ## Installation diff --git a/src/Codeception/Lib/Connector/Symfony.php b/src/Codeception/Lib/Connector/Symfony.php index dafcaa5a..684add44 100644 --- a/src/Codeception/Lib/Connector/Symfony.php +++ b/src/Codeception/Lib/Connector/Symfony.php @@ -20,36 +20,29 @@ class Symfony extends HttpKernelBrowser { - private bool $rebootable; - private bool $hasPerformedRequest = false; private ?ContainerInterface $container; - public array $persistentServices = []; - /** * Constructor. * * @param Kernel $kernel A booted HttpKernel instance - * @param array $services An injected services - * @param bool $rebootable + * @param array $persistentServices An injected services */ - public function __construct(Kernel $kernel, array $services = [], bool $rebootable = true) - { + public function __construct( + Kernel $kernel, + public array $persistentServices = [], + private readonly bool $rebootable = true + ) { parent::__construct($kernel); $this->followRedirects(); - $this->rebootable = $rebootable; - $this->persistentServices = $services; $this->container = $this->getContainer(); $this->rebootKernel(); } - /** - * @param Request $request - * @return Response - */ - protected function doRequest($request): Response + /** @param Request $request */ + protected function doRequest(object $request): Response { if ($this->rebootable) { if ($this->hasPerformedRequest) { diff --git a/src/Codeception/Module/Symfony.php b/src/Codeception/Module/Symfony.php index fc8044ca..bd7b898a 100644 --- a/src/Codeception/Module/Symfony.php +++ b/src/Codeception/Module/Symfony.php @@ -43,7 +43,6 @@ use Symfony\Component\VarDumper\Cloner\Data; use function array_keys; use function array_map; -use function array_merge; use function array_search; use function array_unique; use function class_exists; @@ -74,7 +73,7 @@ * * ## Config * - * ### Symfony 5.x or 4.4 + * ### Symfony 5.4 or higher * * * app_path: 'src' - Specify custom path to your app dir, where the kernel interface is located. * * environment: 'local' - Environment used for load kernel @@ -83,8 +82,8 @@ * * debug: true - Turn on/off debug mode * * cache_router: 'false' - Enable router caching between tests in order to [increase performance](http://lakion.com/blog/how-did-we-speed-up-sylius-behat-suite-with-blackfire) * * rebootable_client: 'true' - Reboot client's kernel before each request - * * guard: 'false' - Enable custom authentication system with guard (only for 4.x and 5.x versions of the symfony) - * * authenticator: 'false' - Reboot client's kernel before each request (only for 6.x versions of the symfony) + * * guard: 'false' - Enable custom authentication system with guard (only for Symfony 5.4) + * * authenticator: 'false' - Reboot client's kernel before each request (only for Symfony 6.0 or higher) * * #### Example (`functional.suite.yml`) - Symfony 4 Directory Structure * @@ -126,7 +125,7 @@ * browser: firefox * ``` * - * If you're using Symfony with Eloquent ORM (instead of Doctrine), you can load the [`ORM` part of Laravel module](https://codeception.com/docs/modules/Laravel5#Parts) + * If you're using Symfony with Eloquent ORM (instead of Doctrine), you can load the [`ORM` part of Laravel module](https://codeception.com/docs/modules/Laravel#Parts) * in addition to Symfony module. * */ @@ -215,7 +214,7 @@ public function _initialize(): void */ public function _before(TestInterface $test): void { - $this->persistentServices = array_merge($this->persistentServices, $this->permanentServices); + $this->persistentServices = [...$this->persistentServices, ...$this->permanentServices]; $this->client = new SymfonyConnector($this->kernel, $this->persistentServices, $this->config['rebootable_client']); } @@ -322,7 +321,7 @@ protected function getKernelClass(): string $this->requireAdditionalAutoloader(); - $filesRealPath = array_map(function ($file) { + $filesRealPath = array_map(static function ($file) { require_once $file; return $file->getRealPath(); }, $results); @@ -331,7 +330,7 @@ protected function getKernelClass(): string if (class_exists($kernelClass)) { $reflectionClass = new ReflectionClass($kernelClass); - if ($file = array_search($reflectionClass->getFileName(), $filesRealPath)) { + if ($file = array_search($reflectionClass->getFileName(), $filesRealPath, true)) { return $kernelClass; } @@ -355,7 +354,7 @@ protected function getProfile(): ?Profile try { $response = $this->getClient()->getResponse(); return $profiler->loadProfileFromResponse($response); - } catch (BadMethodCallException $e) { + } catch (BadMethodCallException) { $this->fail('You must perform a request before using this method.'); } catch (Exception $e) { $this->fail($e->getMessage()); diff --git a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php index 67dc1ddb..001e7ca2 100644 --- a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php @@ -31,7 +31,7 @@ public function rebootClientKernel(): void /** * Verifies that a page is available. - * By default it checks the current page, specify the `$url` parameter to change it. + * By default, it checks the current page, specify the `$url` parameter to change it. * * ```php * seePageRedirectsTo('/admin', '/login'); * ``` - * - * @param string $page - * @param string $redirectsTo */ public function seePageRedirectsTo(string $page, string $redirectsTo): void { diff --git a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php index 0ef1ee4c..8ee296b9 100644 --- a/src/Codeception/Module/Symfony/EventsAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/EventsAssertionsTrait.php @@ -25,7 +25,7 @@ trait EventsAssertionsTrait */ public function dontSeeEvent(array|string $expected = null): void { - $actualEvents = array_merge(array_column($this->getCalledListeners(), 'event')); + $actualEvents = [...array_column($this->getCalledListeners(), 'event')]; $actual = [$this->getOrphanedEvents(), $actualEvents]; $this->assertEventTriggered(false, $expected, $actual); } @@ -110,7 +110,7 @@ public function dontSeeOrphanEvent(array|string $expected = null): void */ public function seeEvent(array|string $expected): void { - $actualEvents = array_merge(array_column($this->getCalledListeners(), 'event')); + $actualEvents = [...array_column($this->getCalledListeners(), 'event')]; $actual = [$this->getOrphanedEvents(), $actualEvents]; $this->assertEventTriggered(true, $expected, $actual); } diff --git a/src/Codeception/Module/Symfony/FormAssertionsTrait.php b/src/Codeception/Module/Symfony/FormAssertionsTrait.php index 31940e15..c6fba53d 100644 --- a/src/Codeception/Module/Symfony/FormAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/FormAssertionsTrait.php @@ -43,7 +43,6 @@ public function dontSeeFormErrors(): void * $I->seeFormErrorMessage('username', 'Username is empty'); * ``` * - * @param string $field * @param string|null $message */ public function seeFormErrorMessage(string $field, string $message = null): void diff --git a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php index 4e61cb2d..f8bb9772 100644 --- a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php @@ -14,8 +14,7 @@ trait MailerAssertionsTrait /** * Checks that no email was sent. * The check is based on `\Symfony\Component\Mailer\EventListener\MessageLoggerListener`, which means: - * If your app performs a HTTP redirect, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first; otherwise this check will *always* pass. - * Starting with version 2.0.0, `codeception/module-symfony` requires your app to use [Symfony Mailer](https://symfony.com/doc/current/mailer.html). If your app still uses [Swift Mailer](https://symfony.com/doc/current/email.html), set your version constraint to `^1.6`. + * If your app performs an HTTP redirect, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first; otherwise this check will *always* pass. */ public function dontSeeEmailIsSent(): void { @@ -25,8 +24,7 @@ public function dontSeeEmailIsSent(): void /** * Checks if the given number of emails was sent (default `$expectedCount`: 1). * The check is based on `\Symfony\Component\Mailer\EventListener\MessageLoggerListener`, which means: - * If your app performs a HTTP redirect after sending the email, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first. - * Starting with version 2.0.0, `codeception/module-symfony` requires your app to use [Symfony Mailer](https://symfony.com/doc/current/mailer.html). If your app still uses [Swift Mailer](https://symfony.com/doc/current/email.html), set your version constraint to `^1.6`. + * If your app performs an HTTP redirect after sending the email, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first. * * ```php * getEvents(); } - $this->fail("codeception/module-symfony requires Symfony Mailer https://symfony.com/doc/current/mailer.html to test emails. If your app still uses Swift Mailer, downgrade codeception/module-symfony to ^1.6 - - - Emails can't be tested without Symfony Mailer service."); + $this->fail("Emails can't be tested without Symfony Mailer service."); } } diff --git a/src/Codeception/Module/Symfony/MimeAssertionsTrait.php b/src/Codeception/Module/Symfony/MimeAssertionsTrait.php index 9228602a..12e73cd8 100644 --- a/src/Codeception/Module/Symfony/MimeAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/MimeAssertionsTrait.php @@ -169,9 +169,9 @@ public function assertEmailTextBodyNotContains(string $text, Email $email = null private function verifyEmailObject(?Email $email, string $function): Email { $email = $email ?: $this->grabLastSentEmail(); - $errorMsgFormat = "There is no email to verify. An Email object was not specified when invoking '%s' and the application has not sent one."; + $errorMsgTemplate = "There is no email to verify. An Email object was not specified when invoking '%s' and the application has not sent one."; return $email ?: $this->fail( - sprintf($errorMsgFormat, $function) + sprintf($errorMsgTemplate, $function) ); } } \ No newline at end of file diff --git a/src/Codeception/Module/Symfony/ParameterAssertionsTrait.php b/src/Codeception/Module/Symfony/ParameterAssertionsTrait.php index 63231dd5..61c98ddd 100644 --- a/src/Codeception/Module/Symfony/ParameterAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ParameterAssertionsTrait.php @@ -5,6 +5,7 @@ namespace Codeception\Module\Symfony; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use UnitEnum; trait ParameterAssertionsTrait { @@ -15,11 +16,8 @@ trait ParameterAssertionsTrait * grabParameter('app.business_name'); * ``` - * - * @param string $parameterName - * @return array|bool|float|int|string|null */ - public function grabParameter(string $parameterName) + public function grabParameter(string $parameterName): array|bool|string|int|float|UnitEnum|null { $parameterBag = $this->grabParameterBagService(); return $parameterBag->get($parameterName); diff --git a/src/Codeception/Module/Symfony/RouterAssertionsTrait.php b/src/Codeception/Module/Symfony/RouterAssertionsTrait.php index e0bdeab0..80501555 100644 --- a/src/Codeception/Module/Symfony/RouterAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/RouterAssertionsTrait.php @@ -5,10 +5,9 @@ namespace Codeception\Module\Symfony; use Symfony\Component\Routing\Exception\ResourceNotFoundException; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouterInterface; use function array_intersect_assoc; -use function array_merge; use function explode; use function sprintf; @@ -23,23 +22,20 @@ trait RouterAssertionsTrait * $I->amOnAction('HomeController'); * $I->amOnAction('ArticleController', ['slug' => 'lorem-ipsum']); * ``` - * - * @param string $action - * @param array $params */ public function amOnAction(string $action, array $params = []): void { $router = $this->grabRouterService(); $routes = $router->getRouteCollection()->getIterator(); + /** @var Route $route */ foreach ($routes as $route) { $controller = $route->getDefault('_controller'); - if (str_ends_with($controller, $action)) { + if (str_ends_with((string) $controller, $action)) { $resource = $router->match($route->getPath()); $url = $router->generate( $resource['_route'], - $params, - UrlGeneratorInterface::ABSOLUTE_PATH + $params ); $this->amOnPage($url); return; @@ -55,9 +51,6 @@ public function amOnAction(string $action, array $params = []): void * $I->amOnRoute('posts.create'); * $I->amOnRoute('posts.show', ['id' => 34]); * ``` - * - * @param string $routeName - * @param array $params */ public function amOnRoute(string $routeName, array $params = []): void { @@ -86,17 +79,16 @@ public function invalidateCachedRouter(): void * $I->seeCurrentActionIs('PostController::index'); * $I->seeCurrentActionIs('HomeController'); * ``` - * - * @param string $action */ public function seeCurrentActionIs(string $action): void { $router = $this->grabRouterService(); $routes = $router->getRouteCollection()->getIterator(); + /** @var Route $route */ foreach ($routes as $route) { $controller = $route->getDefault('_controller'); - if (str_ends_with($controller, $action)) { + if (str_ends_with((string) $controller, $action)) { $request = $this->getClient()->getRequest(); $currentActionFqcn = $request->attributes->get('_controller'); @@ -116,9 +108,6 @@ public function seeCurrentActionIs(string $action): void * $I->seeCurrentRouteIs('posts.index'); * $I->seeCurrentRouteIs('posts.show', ['id' => 8]); * ``` - * - * @param string $routeName - * @param array $params */ public function seeCurrentRouteIs(string $routeName, array $params = []): void { @@ -135,7 +124,7 @@ public function seeCurrentRouteIs(string $routeName, array $params = []): void $this->fail(sprintf('The "%s" url does not match with any route', $uri)); } - $expected = array_merge(['_route' => $routeName], $params); + $expected = ['_route' => $routeName, ...$params]; $intersection = array_intersect_assoc($expected, $match); $this->assertSame($expected, $intersection); @@ -149,8 +138,6 @@ public function seeCurrentRouteIs(string $routeName, array $params = []): void * seeInCurrentRoute('my_blog_pages'); * ``` - * - * @param string $routeName */ public function seeInCurrentRoute(string $routeName): void { diff --git a/src/Codeception/Module/Symfony/SecurityAssertionsTrait.php b/src/Codeception/Module/Symfony/SecurityAssertionsTrait.php index a07e3ab3..b86bd1ff 100644 --- a/src/Codeception/Module/Symfony/SecurityAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/SecurityAssertionsTrait.php @@ -65,7 +65,7 @@ public function seeAuthentication(): void { $security = $this->grabSecurityService(); - if (!$user = $security->getUser()) { + if (!$security->getUser()) { $this->fail('There is no user in session'); } @@ -108,8 +108,6 @@ public function seeRememberedAuthentication(): void * seeUserHasRole('ROLE_ADMIN'); * ``` - * - * @param string $role */ public function seeUserHasRole(string $role): void { diff --git a/src/Codeception/Module/Symfony/SessionAssertionsTrait.php b/src/Codeception/Module/Symfony/SessionAssertionsTrait.php index a8b69afd..7d26314d 100644 --- a/src/Codeception/Module/Symfony/SessionAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/SessionAssertionsTrait.php @@ -178,11 +178,7 @@ protected function getCurrentSession(): SessionInterface { $container = $this->_getContainer(); - if ($this->getSymfonyMajorVersion() < 6) { - return $container->get('session'); - } - - if ($container->has('session')) { + if ($this->getSymfonyMajorVersion() < 6 || $container->has('session')) { return $container->get('session'); } diff --git a/src/Codeception/Module/Symfony/TimeAssertionsTrait.php b/src/Codeception/Module/Symfony/TimeAssertionsTrait.php index 136bef25..a1067f37 100644 --- a/src/Codeception/Module/Symfony/TimeAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TimeAssertionsTrait.php @@ -13,7 +13,7 @@ trait TimeAssertionsTrait /** * Asserts that the time a request lasted is less than expected. * - * If the page performed a HTTP redirect, only the time of the last request will be taken into account. + * If the page performed an HTTP redirect, only the time of the last request will be taken into account. * You can modify this behavior using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first. * * Also, note that using code coverage can significantly increase the time it takes to resolve a request, diff --git a/src/Codeception/Module/Symfony/TwigAssertionsTrait.php b/src/Codeception/Module/Symfony/TwigAssertionsTrait.php index 52b02d0d..1bfba3ec 100644 --- a/src/Codeception/Module/Symfony/TwigAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TwigAssertionsTrait.php @@ -21,7 +21,7 @@ public function dontSeeRenderedTemplate(string $template): void { $twigCollector = $this->grabTwigCollector(__FUNCTION__); - $templates = (array)$twigCollector->getTemplates(); + $templates = $twigCollector->getTemplates(); $this->assertArrayNotHasKey( $template, @@ -42,8 +42,8 @@ public function seeCurrentTemplateIs(string $expectedTemplate): void { $twigCollector = $this->grabTwigCollector(__FUNCTION__); - $templates = (array)$twigCollector->getTemplates(); - $actualTemplate = !empty($templates) ? (string) array_key_first($templates) : 'N/A'; + $templates = $twigCollector->getTemplates(); + $actualTemplate = empty($templates) ? 'N/A' : (string) array_key_first($templates); $this->assertSame( $expectedTemplate, @@ -66,7 +66,7 @@ public function seeRenderedTemplate(string $template): void { $twigCollector = $this->grabTwigCollector(__FUNCTION__); - $templates = (array)$twigCollector->getTemplates(); + $templates = $twigCollector->getTemplates(); $this->assertArrayHasKey( $template,