From e6fbf6fbdd7cc2a2f6f250cf6938fdc3b5114c71 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 17:14:09 +0100 Subject: [PATCH 01/17] chore(cs): use php-cs-fixer with PER2.0 standard --- composer.json | 3 +- src/Codeception/Lib/Connector/Yii2.php | 41 ++++++++++--------- .../Lib/Connector/Yii2/FixturesStore.php | 4 +- src/Codeception/Lib/Connector/Yii2/Logger.php | 12 +++--- .../Lib/Connector/Yii2/TransactionForcer.php | 10 +++-- src/Codeception/Module/Yii2.php | 26 ++++++------ 6 files changed, 52 insertions(+), 44 deletions(-) diff --git a/composer.json b/composer.json index f93903b..dd54a0f 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,8 @@ "codeception/module-asserts": ">= 3.0", "codeception/module-filesystem": "> 3.0", "phpstan/phpstan": "^2", - "rector/rector": "^2" + "rector/rector": "^2", + "friendsofphp/php-cs-fixer": "^3.70" }, "autoload":{ "classmap": ["src/"] diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index d6f89cf..c8031a8 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -32,7 +32,6 @@ use yii\web\Response as YiiResponse; use yii\web\User; - /** * @extends Client */ @@ -40,33 +39,33 @@ class Yii2 extends Client { use Shared\PhpSuperGlobalsConverter; - const CLEAN_METHODS = [ + public const CLEAN_METHODS = [ self::CLEAN_RECREATE, self::CLEAN_CLEAR, self::CLEAN_FORCE_RECREATE, - self::CLEAN_MANUAL + self::CLEAN_MANUAL, ]; /** * Clean the response object by recreating it. * This might lose behaviors / event handlers / other changes that are done in the application bootstrap phase. */ - const CLEAN_RECREATE = 'recreate'; + public const CLEAN_RECREATE = 'recreate'; /** * Same as recreate but will not warn when behaviors / event handlers are lost. */ - const CLEAN_FORCE_RECREATE = 'force_recreate'; + public const CLEAN_FORCE_RECREATE = 'force_recreate'; /** * Clean the response object by resetting specific properties via its' `clear()` method. * This will keep behaviors / event handlers, but could inadvertently leave some changes intact. * @see \yii\web\Response::clear() */ - const CLEAN_CLEAR = 'clear'; + public const CLEAN_CLEAR = 'clear'; /** * Do not clean the response, instead the test writer will be responsible for manually resetting the response in * between requests during one test */ - const CLEAN_MANUAL = 'manual'; + public const CLEAN_MANUAL = 'manual'; /** @@ -257,7 +256,7 @@ function ($matches) use (&$parameters): string { $parameters[$key] = $matches[1] ?? '\w+'; return $key; }, - $template + $template, ); } if ($template === null) { @@ -327,7 +326,7 @@ public function doRequest(object $request): Response $queryString = parse_url($uri, PHP_URL_QUERY); $_SERVER['REQUEST_URI'] = $queryString === null ? $pathString : $pathString . '?' . $queryString; $_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod()); - $_SERVER['QUERY_STRING'] = (string)$queryString; + $_SERVER['QUERY_STRING'] = (string) $queryString; parse_str($queryString ?: '', $params); foreach ($params as $k => $v) { @@ -403,7 +402,7 @@ public function doRequest(object $request): Response protected function encodeCookies( YiiResponse $response, YiiRequest $request, - Security $security + Security $security, ): void { if ($request->enableCookieValidation) { $validationKey = $request->cookieValidationKey; @@ -419,7 +418,7 @@ protected function encodeCookies( : $cookie->value; $value = $security->hashData(serialize($data), $validationKey); } - $expires = is_int($cookie->expire) ? (string)$cookie->expire : null; + $expires = is_int($cookie->expire) ? (string) $cookie->expire : null; $c = new Cookie( $cookie->name, $value, @@ -427,7 +426,7 @@ protected function encodeCookies( $cookie->path, $cookie->domain, $cookie->secure, - $cookie->httpOnly + $cookie->httpOnly, ); $this->getCookieJar()->set($c); } @@ -457,13 +456,15 @@ protected function mockMailer(array $config): array 'class' => TestMailer::class, 'callback' => function (BaseMessage $message): void { $this->emails[] = $message; - } + }, ]; if (isset($config['components'])) { if (!is_array($config['components'])) { - throw new ModuleConfigException($this, - "Yii2 config does not contain components key is not of type array"); + throw new ModuleConfigException( + $this, + "Yii2 config does not contain components key is not of type array", + ); } } else { $config['components'] = []; @@ -538,12 +539,13 @@ protected function resetResponse(Application $app): void || count($app->response->getBehaviors()) > 0) && $method === self::CLEAN_RECREATE ) { - Debug::debug(<<getBehaviors()) > 0 && $method === self::CLEAN_RECREATE) { - Debug::debug(<<pdoCache[$key])) { $connection->pdo = $this->pdoCache[$key]; - } elseif(isset($connection->pdo)) { + } elseif (isset($connection->pdo)) { $this->pdoCache[$key] = $connection->pdo; } - if (isset($this->dsnCache[$connection->dsn]) + if ( + isset($this->dsnCache[$connection->dsn]) && $this->dsnCache[$connection->dsn] !== $key && !$this->ignoreCollidingDSN ) { - $this->debug(<<debug( + <<dsn}) with different configuration. These connections will not see the same database state since we cannot share a transaction between different PDO instances. You can remove this message by adding 'ignoreCollidingDSN = true' in the module configuration. -TEXT +TEXT, ); Debug::pause(); } diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index cfa9eee..8e8c37a 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -316,7 +316,7 @@ private function initServerGlobal(): void 'SCRIPT_NAME' => $entryScript, 'SERVER_NAME' => $parsedUrl['host'] ?? '', 'SERVER_PORT' => $parsedUrl['port'] ?? '80', - 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https' + 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https', ]); } @@ -329,27 +329,27 @@ protected function validateConfig(): void if (!isset($this->config['configFile'])) { throw new ModuleConfigException( self::class, - "The application config file was not configured" + "The application config file was not configured", ); } $pathToConfig = codecept_absolute_path($this->config['configFile']); if (!is_file($pathToConfig)) { throw new ModuleConfigException( self::class, - "The application config file does not exist: " . $pathToConfig + "The application config file does not exist: " . $pathToConfig, ); } $validMethods = implode(", ", Yii2Connector::CLEAN_METHODS); if (!in_array($this->config['responseCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { throw new ModuleConfigException( self::class, - "The response clean method must be one of: " . $validMethods + "The response clean method must be one of: " . $validMethods, ); } if (!in_array($this->config['requestCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { throw new ModuleConfigException( self::class, - "The request clean method must be one of: " . $validMethods + "The request clean method must be one of: " . $validMethods, ); } } @@ -384,7 +384,7 @@ protected function recreateClient(): void 'SCRIPT_NAME' => $entryScript, 'SERVER_NAME' => $parsedUrl['host'] ?? '', 'SERVER_PORT' => $parsedUrl['port'] ?? '80', - 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https' + 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https', ]); $this->validateConfig(); $this->configureClient($this->config); @@ -417,7 +417,8 @@ public function _before(TestInterface $test): void private function loadFixtures(object $test): void { $this->debugSection('Fixtures', 'Loading fixtures'); - if ($this->loadedFixtures === [] + if ( + $this->loadedFixtures === [] && method_exists($test, $this->config['fixturesMethod']) ) { $connectionWatcher = new ConnectionWatcher(); @@ -517,7 +518,7 @@ public function amLoggedInAs(int|string|IdentityInterface $user): void { try { $this->getClient()->findAndLoginUser($user); - } catch (ConfigurationException|RuntimeException $e) { + } catch (ConfigurationException | RuntimeException $e) { throw new ModuleException($this, $e->getMessage()); } } @@ -579,8 +580,8 @@ public function haveFixtures(array $fixtures): void public function grabFixtures(): array { $result = []; - foreach($this->loadedFixtures as $store) { - foreach($store->getFixtures() as $name => $fixture) { + foreach ($this->loadedFixtures as $store) { + foreach ($store->getFixtures() as $name => $fixture) { $result[$name] = $fixture; } } @@ -616,7 +617,7 @@ public function grabFixture(string $name, null|string $index = null): Fixture|\y return match (true) { $index === null => $fixture, $fixture instanceof \yii\test\BaseActiveFixture => $fixture->getModel($index), - default => throw new ModuleException($this, "Fixture $name is not an instance of ActiveFixture and can't be loaded with second parameter") + default => throw new ModuleException($this, "Fixture $name is not an instance of ActiveFixture and can't be loaded with second parameter"), }; } @@ -713,7 +714,8 @@ protected function findRecord(string $model, array $attributes = []): ActiveReco throw new RuntimeException("Class $model does not exist"); } $rc = new ReflectionClass($model); - if ($rc->hasMethod('find') + if ( + $rc->hasMethod('find') /** @phpstan-ignore-next-line */ && ($findMethod = $rc->getMethod('find')) && $findMethod->isStatic() From 08c67d5684a240e9a890e9070c484177100dbe28 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 17:14:45 +0100 Subject: [PATCH 02/17] chore: update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 31a345f..151aca9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tests/_support tests/_output tests/cases/yii2-app-advanced/_data/db.sqlite +.php-cs-fixer.cache \ No newline at end of file From ceb78d23e2710cee56e7d2e5cf9c53ec59217925 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 17:15:09 +0100 Subject: [PATCH 03/17] chore: apply cs to tests --- tests/bootstrap.php | 1 + tests/cases/closeConnections/config.php | 3 ++- .../fixtures/EmptyFixture.php | 10 +++------- .../functional/FixturesCest.php | 2 +- .../functional/FixturesInBeforeCest.php | 2 +- .../functional/NoFixturesCest.php | 3 +-- .../helpers/SqlliteHelper.php | 4 ++-- .../closeConnectionsNoCleanup/config.php | 3 ++- .../functional/FixturesCest.php | 2 +- .../functional/FixturesInBeforeCest.php | 2 +- .../functional/ThirdCest.php | 2 +- tests/cases/events/config.php | 19 ++++++++++--------- .../events/controllers/SiteController.php | 9 +++------ .../cases/events/functional/ResponseCest.php | 19 +++++++++---------- tests/cases/locale-urls/config.php | 12 ++++++------ .../controllers/SiteController.php | 4 ++-- .../locale-urls/functional/LocaleUrlCest.php | 1 - tests/cases/mock-mailer/config.php | 3 ++- .../controllers/UserController.php | 4 ++-- .../functional/PageCest.php | 6 +++--- tests/cases/simple/config.php | 12 ++++++------ .../simple/controllers/SiteController.php | 6 +++--- tests/cases/simple/functional/SimpleCest.php | 7 +++---- tests/cases/simple/helpers/DummyUser.php | 4 +--- tests/cases/simple/helpers/EmptyString.php | 2 +- tests/cases/sqlite/config.php | 15 ++++++++------- tests/cases/sqlite/fixtures/TestFixture.php | 12 ++++++------ tests/cases/sqlite/functional/SqLiteCest.php | 2 +- tests/cases/yii2-app-advanced/config.php | 12 ++++++------ tests/cases/yii2-app-advanced/migrate.php | 6 +++--- 30 files changed, 91 insertions(+), 98 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 5ce6bee..d6b21e6 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,4 +1,5 @@ 'Simple', 'basePath' => __DIR__, @@ -9,4 +10,4 @@ 'dsn' => 'sqlite:' . \tests\helpers\SqlliteHelper::getTmpFile(), ], ], -]; \ No newline at end of file +]; diff --git a/tests/cases/closeConnections/fixtures/EmptyFixture.php b/tests/cases/closeConnections/fixtures/EmptyFixture.php index 9006bb0..34bf498 100644 --- a/tests/cases/closeConnections/fixtures/EmptyFixture.php +++ b/tests/cases/closeConnections/fixtures/EmptyFixture.php @@ -8,11 +8,7 @@ class EmptyFixture extends DbFixture { - public function load() - { - } + public function load() {} - public function unload() - { - } -} \ No newline at end of file + public function unload() {} +} diff --git a/tests/cases/closeConnections/functional/FixturesCest.php b/tests/cases/closeConnections/functional/FixturesCest.php index 1c034cf..a834b53 100644 --- a/tests/cases/closeConnections/functional/FixturesCest.php +++ b/tests/cases/closeConnections/functional/FixturesCest.php @@ -34,4 +34,4 @@ public function NoConnections(FunctionalTester $I, Example $example) $I->assertSame(SqlliteHelper::connectionCount(), $example['count']); } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnections/functional/FixturesInBeforeCest.php b/tests/cases/closeConnections/functional/FixturesInBeforeCest.php index 711d587..3210b7b 100644 --- a/tests/cases/closeConnections/functional/FixturesInBeforeCest.php +++ b/tests/cases/closeConnections/functional/FixturesInBeforeCest.php @@ -34,4 +34,4 @@ public function NoConnections(FunctionalTester $I, Example $example) $I->assertSame(SqlliteHelper::connectionCount(), $example['count']); } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnections/functional/NoFixturesCest.php b/tests/cases/closeConnections/functional/NoFixturesCest.php index 2ff5b40..523336d 100644 --- a/tests/cases/closeConnections/functional/NoFixturesCest.php +++ b/tests/cases/closeConnections/functional/NoFixturesCest.php @@ -10,7 +10,6 @@ class NoFixturesCest { - protected function numberProvider() { return array_pad([], 5, ['count' => 0]); @@ -24,4 +23,4 @@ public function NoConnections(FunctionalTester $I, Example $example) { $I->assertSame(SqlliteHelper::connectionCount(), $example['count']); } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnections/helpers/SqlliteHelper.php b/tests/cases/closeConnections/helpers/SqlliteHelper.php index 15f263a..70e662c 100644 --- a/tests/cases/closeConnections/helpers/SqlliteHelper.php +++ b/tests/cases/closeConnections/helpers/SqlliteHelper.php @@ -20,7 +20,7 @@ public static function connectionCount() { $path = self::$temp_name; $count = shell_exec("lsof -w {$path} | grep {$path} | wc -l"); - return (int)$count; + return (int) $count; } public static function debug() @@ -31,4 +31,4 @@ public static function debug() codecept_debug(shell_exec($cmd)); } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnectionsNoCleanup/config.php b/tests/cases/closeConnectionsNoCleanup/config.php index 99e2245..cf9fdb8 100644 --- a/tests/cases/closeConnectionsNoCleanup/config.php +++ b/tests/cases/closeConnectionsNoCleanup/config.php @@ -1,4 +1,5 @@ 'Simple', 'basePath' => __DIR__, @@ -9,4 +10,4 @@ 'dsn' => 'sqlite:' . \tests\helpers\SqlliteHelper::getTmpFile(), ], ], -]; \ No newline at end of file +]; diff --git a/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php b/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php index 92a5016..1fbe3e8 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php @@ -37,4 +37,4 @@ public function NoConnections3(FunctionalTester $I) $I->assertSame(0, $count); } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php b/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php index bdf0241..3edc2c9 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php @@ -36,4 +36,4 @@ public function OnlyOneConnection3(FunctionalTester $I) $count = SqlliteHelper::connectionCount(); $I->assertSame(1, $count); } -} \ No newline at end of file +} diff --git a/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php b/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php index 1a9caa2..f109ab0 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php @@ -39,4 +39,4 @@ public function OnlyOneConnection3(FunctionalTester $I) $count = SqlliteHelper::connectionCount(); $I->assertSame(1, $count); } -} \ No newline at end of file +} diff --git a/tests/cases/events/config.php b/tests/cases/events/config.php index 09869fd..678f8db 100644 --- a/tests/cases/events/config.php +++ b/tests/cases/events/config.php @@ -1,22 +1,23 @@ 'Response Events', 'basePath' => __DIR__, 'bootstrap' => [ - function(\yii\web\Application $application) { - $application->response->on(\yii\web\Response::EVENT_BEFORE_SEND, function($event) use ($application) { + function (\yii\web\Application $application) { + $application->response->on(\yii\web\Response::EVENT_BEFORE_SEND, function ($event) use ($application) { $application->trigger('responseBeforeSendBootstrap'); }); - } + }, ], 'components' => [ 'request' => [ - 'cookieValidationKey' => 'secret' + 'cookieValidationKey' => 'secret', ], 'response' => [ - 'on beforeSend' => function() { + 'on beforeSend' => function () { \Yii::$app->trigger('responseBeforeSendConfig'); - } - ] - ] -]; \ No newline at end of file + }, + ], + ], +]; diff --git a/tests/cases/events/controllers/SiteController.php b/tests/cases/events/controllers/SiteController.php index 9c640e7..c5a90ce 100644 --- a/tests/cases/events/controllers/SiteController.php +++ b/tests/cases/events/controllers/SiteController.php @@ -3,13 +3,10 @@ declare(strict_types=1); namespace app\controllers; + use yii\web\Controller; class SiteController extends Controller { - - public function actionIndex() - { - - } -} \ No newline at end of file + public function actionIndex() {} +} diff --git a/tests/cases/events/functional/ResponseCest.php b/tests/cases/events/functional/ResponseCest.php index 360b888..2f57301 100644 --- a/tests/cases/events/functional/ResponseCest.php +++ b/tests/cases/events/functional/ResponseCest.php @@ -9,14 +9,13 @@ class ResponseCest { - public function testAfterSend(FunctionalTester $I) { $sources = []; - \Yii::$app->on('responseBeforeSendConfig', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendConfig', function (Event $event) use (&$sources) { $sources[] = 'config'; }); - \Yii::$app->on('responseBeforeSendBootstrap', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendBootstrap', function (Event $event) use (&$sources) { $sources[] = 'bootstrap'; }); $I->assertEmpty($sources); @@ -32,14 +31,14 @@ public function testAfterSend(FunctionalTester $I) public function testAfterSendWithRecreate(FunctionalTester $I, \Codeception\Module\Yii2 $module) { $module->_reconfigure([ - 'responseCleanMethod' => Yii2::CLEAN_RECREATE + 'responseCleanMethod' => Yii2::CLEAN_RECREATE, ]); $module->client->startApp(); $sources = []; - \Yii::$app->on('responseBeforeSendConfig', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendConfig', function (Event $event) use (&$sources) { $sources[] = 'config'; }); - \Yii::$app->on('responseBeforeSendBootstrap', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendBootstrap', function (Event $event) use (&$sources) { $sources[] = 'bootstrap'; }); $I->assertEmpty($sources); @@ -57,14 +56,14 @@ public function testAfterSendWithRecreate(FunctionalTester $I, \Codeception\Modu public function testAfterSendWithForcedRecreate(FunctionalTester $I, \Codeception\Module\Yii2 $module) { $module->_reconfigure([ - 'responseCleanMethod' => Yii2::CLEAN_FORCE_RECREATE + 'responseCleanMethod' => Yii2::CLEAN_FORCE_RECREATE, ]); $module->client->startApp(); $sources = []; - \Yii::$app->on('responseBeforeSendConfig', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendConfig', function (Event $event) use (&$sources) { $sources[] = 'config'; }); - \Yii::$app->on('responseBeforeSendBootstrap', function(Event $event) use (&$sources) { + \Yii::$app->on('responseBeforeSendBootstrap', function (Event $event) use (&$sources) { $sources[] = 'bootstrap'; }); @@ -83,4 +82,4 @@ public function testAfterSendWithForcedRecreate(FunctionalTester $I, \Codeceptio $I->assertSame(['config'], $sources); } -} \ No newline at end of file +} diff --git a/tests/cases/locale-urls/config.php b/tests/cases/locale-urls/config.php index 101c377..9dae22c 100644 --- a/tests/cases/locale-urls/config.php +++ b/tests/cases/locale-urls/config.php @@ -9,7 +9,7 @@ 'components' => [ 'request' => [ 'enableCsrfValidation' => false, - 'cookieValidationKey' => 'test' + 'cookieValidationKey' => 'test', ], 'urlManager' => [ 'class' => UrlManager::class, @@ -17,15 +17,15 @@ 'showScriptName' => false, 'enableLanguagePersistence' => false, 'enableLocaleUrls' => true, - 'languages' => ['nl', 'en'] - ] + 'languages' => ['nl', 'en'], + ], ], - 'on beforeRequest' => function(\yii\base\Event $event) { + 'on beforeRequest' => function (\yii\base\Event $event) { $app = $event->sender; if ($app->has('urlManager', true)) { $config = $app->getComponents()['urlManager']; \Codeception\Util\Debug::debug('Resetting url manager: ' . print_r($config, true)); $app->set('urlManager', $config); } - } -]; \ No newline at end of file + }, +]; diff --git a/tests/cases/locale-urls/controllers/SiteController.php b/tests/cases/locale-urls/controllers/SiteController.php index c26c1bf..979579d 100644 --- a/tests/cases/locale-urls/controllers/SiteController.php +++ b/tests/cases/locale-urls/controllers/SiteController.php @@ -3,12 +3,12 @@ declare(strict_types=1); namespace app\localeurls\controllers; + use yii\helpers\Url; use yii\web\Controller; class SiteController extends Controller { - public function actionForm() { $action = Url::to(['site/post']); @@ -32,4 +32,4 @@ public function actionPost() \Yii::$app->response->statusCode = 201; return print_r(\Yii::$app->request->bodyParams, true); } -} \ No newline at end of file +} diff --git a/tests/cases/locale-urls/functional/LocaleUrlCest.php b/tests/cases/locale-urls/functional/LocaleUrlCest.php index 96c431a..61998c1 100644 --- a/tests/cases/locale-urls/functional/LocaleUrlCest.php +++ b/tests/cases/locale-urls/functional/LocaleUrlCest.php @@ -8,7 +8,6 @@ class LocaleUrlCest { - public function testInstantiation(FunctionalTester $I) { $I->assertInstanceOf(Application::class, \Yii::$app); diff --git a/tests/cases/mock-mailer/config.php b/tests/cases/mock-mailer/config.php index 5b33dcc..10a2778 100644 --- a/tests/cases/mock-mailer/config.php +++ b/tests/cases/mock-mailer/config.php @@ -1,4 +1,5 @@ 'Simple', 'basePath' => __DIR__, @@ -6,7 +7,7 @@ 'components' => [ 'request' => [ 'enableCsrfValidation' => false, - 'cookieValidationKey' => 'test' + 'cookieValidationKey' => 'test', ], 'mailer' => [ 'class' => 'yii\symfonymailer\Mailer', diff --git a/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php b/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php index 35195eb..df2bf54 100644 --- a/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php +++ b/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php @@ -12,8 +12,8 @@ public function behaviors() { return [ 'cache' => [ - 'class' => PageCache::class - ] + 'class' => PageCache::class, + ], ]; } diff --git a/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php b/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php index dda305f..43dcf3c 100644 --- a/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php +++ b/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php @@ -2,8 +2,8 @@ declare(strict_types=1); -class PageCest { - +class PageCest +{ public function testCache(\tests\FunctionalTester $I) { $I->amOnRoute('user/index'); @@ -12,4 +12,4 @@ public function testCache(\tests\FunctionalTester $I) $I->amOnRoute('user/index'); $I->canSeeResponseCodeIs(200); } -} \ No newline at end of file +} diff --git a/tests/cases/simple/config.php b/tests/cases/simple/config.php index 2e11ee3..3f7d633 100644 --- a/tests/cases/simple/config.php +++ b/tests/cases/simple/config.php @@ -9,15 +9,15 @@ 'components' => [ 'request' => [ 'enableCsrfValidation' => false, - 'cookieValidationKey' => 'test' + 'cookieValidationKey' => 'test', ], 'user' => [ - 'identityClass' => DummyUser::class - ] + 'identityClass' => DummyUser::class, + ], ], - 'on beforeRequest' => function() { + 'on beforeRequest' => function () { if (isset(\Yii::$app->params['throw'])) { throw \Yii::$app->params['throw']; } - } -]; \ No newline at end of file + }, +]; diff --git a/tests/cases/simple/controllers/SiteController.php b/tests/cases/simple/controllers/SiteController.php index c8c02ef..7a0245e 100644 --- a/tests/cases/simple/controllers/SiteController.php +++ b/tests/cases/simple/controllers/SiteController.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace app\simple\controllers; + use app\simple\helpers\EmptyString; use yii\base\Action; use yii\helpers\Url; @@ -10,7 +11,6 @@ class SiteController extends Controller { - public function actionForm() { $action = Url::to(['site/post']); @@ -52,7 +52,7 @@ public function actionEnd() * @return bool * @throws \yii\web\BadRequestHttpException */ - public function beforeAction($action) + public function beforeAction($action) { if ($action->id === 'empty-response') { \Yii::$app->response->stream = fopen('php://memory', 'r+'); @@ -71,4 +71,4 @@ public function actionIndex() { return ''; } -} \ No newline at end of file +} diff --git a/tests/cases/simple/functional/SimpleCest.php b/tests/cases/simple/functional/SimpleCest.php index 7306fd7..0bb27b3 100644 --- a/tests/cases/simple/functional/SimpleCest.php +++ b/tests/cases/simple/functional/SimpleCest.php @@ -10,7 +10,6 @@ class SimpleCest { - public function testInstantiation(FunctionalTester $I) { $I->assertInstanceOf(Application::class, \Yii::$app); @@ -38,7 +37,7 @@ public function testFormSubmit2(FunctionalTester $I) public function testException(FunctionalTester $I) { - $I->expectThrowable(new \Exception('This is not an HttpException'), function() use ($I) { + $I->expectThrowable(new \Exception('This is not an HttpException'), function () use ($I) { $I->amOnRoute('/site/exception'); }); $I->assertInstanceOf(Application::class, \Yii::$app); @@ -48,7 +47,7 @@ public function testExceptionInBeforeRequest(FunctionalTester $I) { $e = new \Exception('This is not an HttpException'); \Yii::$app->params['throw'] = $e; - $I->expectThrowable($e, function() use ($I) { + $I->expectThrowable($e, function () use ($I) { $I->amOnRoute('/site/exception'); }); } @@ -67,7 +66,7 @@ public function testEmptyResponse(FunctionalTester $I) public function testMissingUser(FunctionalTester $I) { - $I->expectThrowable(ModuleException::class, function() use ($I) { + $I->expectThrowable(ModuleException::class, function () use ($I) { $I->amLoggedInAs('nobody'); }); $I->amOnRoute('/site/index'); diff --git a/tests/cases/simple/helpers/DummyUser.php b/tests/cases/simple/helpers/DummyUser.php index 8b00bd1..f3e9842 100644 --- a/tests/cases/simple/helpers/DummyUser.php +++ b/tests/cases/simple/helpers/DummyUser.php @@ -4,12 +4,10 @@ namespace app\simple\helpers; - use yii\web\IdentityInterface; class DummyUser implements IdentityInterface { - /** * Finds an identity by the given ID. * @param string|int $id the ID to be looked for @@ -75,4 +73,4 @@ public function validateAuthKey($authKey) { // TODO: Implement validateAuthKey() method. } -} \ No newline at end of file +} diff --git a/tests/cases/simple/helpers/EmptyString.php b/tests/cases/simple/helpers/EmptyString.php index feeb241..9cd51cf 100644 --- a/tests/cases/simple/helpers/EmptyString.php +++ b/tests/cases/simple/helpers/EmptyString.php @@ -25,4 +25,4 @@ public function __toString() { return ''; } -} \ No newline at end of file +} diff --git a/tests/cases/sqlite/config.php b/tests/cases/sqlite/config.php index e20ed92..363a298 100644 --- a/tests/cases/sqlite/config.php +++ b/tests/cases/sqlite/config.php @@ -1,4 +1,5 @@ 'Simple', 'basePath' => __DIR__, @@ -6,19 +7,19 @@ 'components' => [ 'db' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . tempnam(null, '/file0') + 'dsn' => 'sqlite:' . tempnam(null, '/file0'), ], 'db1' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . tempnam(null, '/file1') + 'dsn' => 'sqlite:' . tempnam(null, '/file1'), ], 'db21' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . ($name = tempnam(null, '/file2')) + 'dsn' => 'sqlite:' . ($name = tempnam(null, '/file2')), ], 'db22' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . $name - ] - ] -]; \ No newline at end of file + 'dsn' => 'sqlite:' . $name, + ], + ], +]; diff --git a/tests/cases/sqlite/fixtures/TestFixture.php b/tests/cases/sqlite/fixtures/TestFixture.php index 04c66a9..e781476 100644 --- a/tests/cases/sqlite/fixtures/TestFixture.php +++ b/tests/cases/sqlite/fixtures/TestFixture.php @@ -4,7 +4,6 @@ namespace tests\fixtures; - use yii\db\Connection; use yii\db\Exception; use yii\test\DbFixture; @@ -13,13 +12,14 @@ class TestFixture extends DbFixture { public $tableName = 'test'; public $tableConfig = [ - 'id' => 'int' + 'id' => 'int', ]; public $dbComponents = []; - public function load() { - foreach($this->dbComponents as $name) { + public function load() + { + foreach ($this->dbComponents as $name) { /** @var Connection $connection */ $connection = \Yii::$app->get($name); $connection->createCommand()->createTable($this->tableName, $this->tableConfig)->execute(); @@ -28,7 +28,7 @@ public function load() { public function unload() { - foreach($this->dbComponents as $name) { + foreach ($this->dbComponents as $name) { /** @var Connection $connection */ $connection = \Yii::$app->get($name); if (in_array($this->tableName, $connection->getSchema()->getTableNames('', true))) { @@ -36,4 +36,4 @@ public function unload() } } } -} \ No newline at end of file +} diff --git a/tests/cases/sqlite/functional/SqLiteCest.php b/tests/cases/sqlite/functional/SqLiteCest.php index ea3b4c7..732aefa 100644 --- a/tests/cases/sqlite/functional/SqLiteCest.php +++ b/tests/cases/sqlite/functional/SqLiteCest.php @@ -17,7 +17,7 @@ public function _fixtures() return [ [ 'class' => TestFixture::class, - 'dbComponents' => ['db1', 'db21'] + 'dbComponents' => ['db1', 'db21'], ], ]; } diff --git a/tests/cases/yii2-app-advanced/config.php b/tests/cases/yii2-app-advanced/config.php index 82c61da..7c2abde 100644 --- a/tests/cases/yii2-app-advanced/config.php +++ b/tests/cases/yii2-app-advanced/config.php @@ -10,13 +10,13 @@ 'components' => [ 'db' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . codecept_output_dir('/db.sqlite') + 'dsn' => 'sqlite:' . codecept_output_dir('/db.sqlite'), ], 'request' => [ - 'cookieValidationKey' => 'test' + 'cookieValidationKey' => 'test', ], 'assetManager' => [ - 'basePath' => sys_get_temp_dir() + 'basePath' => sys_get_temp_dir(), ], 'mailer' => [ 'viewPath' => '@common/mail', @@ -24,9 +24,9 @@ ], 'aliases' => [ '@bower' => '@vendor/bower-asset', - '@npm' => '@vendor/npm-asset' + '@npm' => '@vendor/npm-asset', ], - 'vendorPath' => __DIR__ . '/../../../vendor' + 'vendorPath' => __DIR__ . '/../../../vendor', ]); -return $config; \ No newline at end of file +return $config; diff --git a/tests/cases/yii2-app-advanced/migrate.php b/tests/cases/yii2-app-advanced/migrate.php index a910536..97d9df1 100644 --- a/tests/cases/yii2-app-advanced/migrate.php +++ b/tests/cases/yii2-app-advanced/migrate.php @@ -8,10 +8,10 @@ 'components' => [ 'db' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . __DIR__ . '/_data/db.sqlite' - ], + 'dsn' => 'sqlite:' . __DIR__ . '/_data/db.sqlite', + ], ], - 'vendorPath' => __DIR__ . '/../../../vendor' + 'vendorPath' => __DIR__ . '/../../../vendor', ]); return $config; From ac7d74a2cd23f5a8ccd80a13ba2f4b91746e4ad3 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 25 Feb 2025 11:49:20 +0100 Subject: [PATCH 04/17] chore: use easy-coding-standard --- composer.json | 3 +- phpstan.neon | 10 +- src/Codeception/Lib/Connector/Yii2.php | 82 +++++++------ .../Lib/Connector/Yii2/ConnectionWatcher.php | 10 +- .../Lib/Connector/Yii2/FixturesStore.php | 7 +- src/Codeception/Lib/Connector/Yii2/Logger.php | 22 ++-- .../Lib/Connector/Yii2/TestMailer.php | 2 +- .../Lib/Connector/Yii2/TransactionForcer.php | 41 ++++--- src/Codeception/Module/Yii2.php | 108 ++++++++++-------- tests/bootstrap.php | 4 +- tests/cases/closeConnections/config.php | 2 + .../fixtures/EmptyFixture.php | 10 +- .../functional/FixturesCest.php | 4 +- .../functional/FixturesInBeforeCest.php | 4 +- .../functional/NoFixturesCest.php | 3 +- .../helpers/SqlliteHelper.php | 3 +- .../closeConnectionsNoCleanup/config.php | 2 + .../functional/FixturesCest.php | 5 +- .../functional/FixturesInBeforeCest.php | 4 +- .../functional/ThirdCest.php | 4 +- tests/cases/events/config.php | 2 + .../events/controllers/SiteController.php | 6 +- .../cases/events/functional/ResponseCest.php | 2 +- tests/cases/locale-urls/config.php | 2 + .../controllers/SiteController.php | 2 +- .../locale-urls/functional/LocaleUrlCest.php | 4 +- tests/cases/mock-mailer/config.php | 2 + .../pageCacheHeaderAlreadySent/config.php | 2 + .../controllers/UserController.php | 2 +- .../functional/PageCest.php | 2 +- tests/cases/simple/config.php | 2 + .../simple/controllers/SiteController.php | 3 +- tests/cases/simple/functional/SimpleCest.php | 3 +- tests/cases/simple/helpers/DummyUser.php | 2 +- tests/cases/simple/helpers/EmptyString.php | 2 +- tests/cases/sqlite/config.php | 2 + tests/cases/sqlite/fixtures/TestFixture.php | 4 +- tests/cases/sqlite/functional/SqLiteCest.php | 2 +- tests/cases/yii2-app-advanced/config.php | 2 + tests/cases/yii2-app-advanced/migrate.php | 2 + 40 files changed, 222 insertions(+), 158 deletions(-) diff --git a/composer.json b/composer.json index dd54a0f..825d849 100644 --- a/composer.json +++ b/composer.json @@ -26,12 +26,11 @@ "yiisoft/yii2": "dev-master", "yiisoft/yii2-app-advanced": "dev-master", "codeception/verify": "^3.0", - "codemix/yii2-localeurls": "^1.7", "codeception/module-asserts": ">= 3.0", "codeception/module-filesystem": "> 3.0", "phpstan/phpstan": "^2", "rector/rector": "^2", - "friendsofphp/php-cs-fixer": "^3.70" + "symplify/easy-coding-standard": "^12.5" }, "autoload":{ "classmap": ["src/"] diff --git a/phpstan.neon b/phpstan.neon index 9b7e02f..2b46b93 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,12 @@ #includes: # - phpstan-baseline.neon parameters: + exceptions: + check: + missingCheckedExceptionInThrows: false + reportUncheckedExceptionDeadCatch: false reportUnmatchedIgnoredErrors: true + editorUrl: "phpstorm://open?file=%%file%%&line=%%line%%" dynamicConstantNames: - CONSOLE - YII_DEBUG @@ -9,10 +14,11 @@ parameters: paths: - src checkMaybeUndefinedVariables: true + treatPhpDocTypesAsCertain: false ignoreErrors: # All Yii setters accept `null` but their phpdoc is incorrect. - - message: '~^Parameter #1 \$(.+) of method yii\\web\\Request::set(.+)\(\) expects (.+), null given.$~' - path: 'src/' +# - message: '~^Parameter #1 \$(.+) of method yii\\web\\Request::set(.+)\(\) expects (.+), null given.$~' +# path: 'src/' # If you want to ignore missing generics errors in the future, you can add: # - identifier: missingType.generics stubFiles: diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index c8031a8..80b1b87 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -15,7 +15,6 @@ use Symfony\Component\BrowserKit\CookieJar; use Symfony\Component\BrowserKit\History; use Symfony\Component\BrowserKit\Request as BrowserkitRequest; -use yii\web\Request as YiiRequest; use Symfony\Component\BrowserKit\Response; use Yii; use yii\base\Component; @@ -24,18 +23,17 @@ use yii\base\Security; use yii\base\UserException; use yii\mail\BaseMessage; -use yii\mail\MessageInterface; use yii\web\Application; -use yii\web\ErrorHandler; use yii\web\IdentityInterface; use yii\web\Request; +use yii\web\Request as YiiRequest; use yii\web\Response as YiiResponse; use yii\web\User; /** * @extends Client */ -class Yii2 extends Client +final class Yii2 extends Client { use Shared\PhpSuperGlobalsConverter; @@ -45,18 +43,22 @@ class Yii2 extends Client self::CLEAN_FORCE_RECREATE, self::CLEAN_MANUAL, ]; + /** * Clean the response object by recreating it. * This might lose behaviors / event handlers / other changes that are done in the application bootstrap phase. */ public const CLEAN_RECREATE = 'recreate'; + /** * Same as recreate but will not warn when behaviors / event handlers are lost. */ public const CLEAN_FORCE_RECREATE = 'force_recreate'; + /** * Clean the response object by resetting specific properties via its' `clear()` method. * This will keep behaviors / event handlers, but could inadvertently leave some changes intact. + * * @see \yii\web\Response::clear() */ public const CLEAN_CLEAR = 'clear'; @@ -67,7 +69,6 @@ class Yii2 extends Client */ public const CLEAN_MANUAL = 'manual'; - /** * @var string application config file */ @@ -91,6 +92,7 @@ class Yii2 extends Client /** * This option is there primarily for backwards compatibility. * It means you cannot make any modification to application state inside your app, since they will get discarded. + * * @var bool whether to recreate the whole application before each request */ public $recreateApplication = false; @@ -106,7 +108,6 @@ class Yii2 extends Client */ public string|null $applicationClass = null; - /** * @var list */ @@ -117,7 +118,7 @@ class Yii2 extends Client */ protected function getApplication(): \yii\base\Application { - if (!isset(Yii::$app)) { + if (! isset(Yii::$app)) { $this->startApp(); } return Yii::$app ?? throw new \RuntimeException('Failed to create Yii2 application'); @@ -126,7 +127,7 @@ protected function getApplication(): \yii\base\Application private function getWebRequest(): YiiRequest { $request = $this->getApplication()->request; - if (!$request instanceof YiiRequest) { + if (! $request instanceof YiiRequest) { throw new \RuntimeException('Request component is not of type ' . YiiRequest::class); } return $request; @@ -148,15 +149,16 @@ public function resetApplication(bool $closeSession = true): void /** * Finds and logs in a user + * * @internal - * @throws ConfigurationException - * @throws \RuntimeException + * @throws ConfigurationException + * @throws \RuntimeException */ public function findAndLoginUser(int|string|IdentityInterface $user): void { $app = $this->getApplication(); $userComponent = $app->get('user'); - if (!$userComponent instanceof User) { + if (! $userComponent instanceof User) { throw new ConfigurationException('The user component is not configured'); } @@ -175,14 +177,14 @@ public function findAndLoginUser(int|string|IdentityInterface $user): void /** * @internal - * @param string $name The name of the cookie - * @param string $value The value of the cookie - * @return string The value to send to the browser + * @param string $name The name of the cookie + * @param string $value The value of the cookie + * @return string The value to send to the browser */ public function hashCookieData(string $name, string $value): string { $request = $this->getWebRequest(); - if (!$request->enableCookieValidation) { + if (! $request->enableCookieValidation) { return $value; } return $this->getApplication()->security->hashData(serialize([$name, $value]), $request->cookieValidationKey); @@ -190,7 +192,7 @@ public function hashCookieData(string $name, string $value): string /** * @internal - * @return non-empty-list List of regex patterns for recognized domain names + * @return non-empty-list List of regex patterns for recognized domain names */ public function getInternalDomains(): array { @@ -199,7 +201,9 @@ public function getInternalDomains(): array $domains = [$this->getDomainRegex($urlManager->hostInfo)]; if ($urlManager->enablePrettyUrl) { foreach ($urlManager->rules as $rule) { - /** @var \yii\web\UrlRule $rule */ + /** + * @var \yii\web\UrlRule $rule +*/ if ($rule->host !== null) { $domains[] = $this->getDomainRegex($rule->host); } @@ -210,7 +214,7 @@ public function getInternalDomains(): array /** * @internal - * @return list List of sent emails + * @return list List of sent emails */ public function getEmails(): array { @@ -219,6 +223,7 @@ public function getEmails(): array /** * Deletes all stored emails. + * * @internal */ public function clearEmails(): void @@ -233,7 +238,7 @@ public function getComponent(string $name): object|null { $app = $this->getApplication(); $result = $app->get($name, false); - if (!isset($result)) { + if (! isset($result)) { throw new ConfigurationException("Component $name is not available in current application"); } return $result; @@ -269,6 +274,7 @@ function ($matches) use (&$parameters): string { /** * Gets the name of the CSRF param. + * * @internal */ public function getCsrfParamName(): string @@ -279,8 +285,8 @@ public function getCsrfParamName(): string public function startApp(?\yii\log\Logger $logger = null): void { codecept_debug('Starting application'); - $config = require($this->configFile); - if (!isset($config['class'])) { + $config = include $this->configFile; + if (! isset($config['class'])) { $config['class'] = $this->applicationClass ?? \yii\web\Application::class; } @@ -291,7 +297,7 @@ public function startApp(?\yii\log\Logger $logger = null): void $config = $this->mockMailer($config); $app = Yii::createObject($config); - if (!$app instanceof \yii\base\Application) { + if (! $app instanceof \yii\base\Application) { throw new ModuleConfigException($this, "Failed to initialize Yii2 app"); } \Yii::$app = $app; @@ -338,7 +344,7 @@ public function doRequest(object $request): Response $this->beforeRequest(); $app = $this->getApplication(); - if (!$app instanceof Application) { + if (! $app instanceof Application) { throw new ConfigurationException("Application is not a web application"); } @@ -372,7 +378,7 @@ public function doRequest(object $request): Response // to expect error response codes in tests. $app->errorHandler->discardExistingOutput = false; $app->errorHandler->handleException($e); - } elseif (!$e instanceof ExitException) { + } elseif (! $e instanceof ExitException) { // for exceptions not related to Http, we pass them to Codeception throw $e; } @@ -386,7 +392,7 @@ public function doRequest(object $request): Response } $content = ob_get_clean(); - if (empty($content) && !empty($yiiResponse->content) && !isset($yiiResponse->stream)) { + if (empty($content) && ! empty($yiiResponse->content) && ! isset($yiiResponse->stream)) { throw new \RuntimeException('No content was sent from Yii application'); } elseif ($content === false) { throw new \RuntimeException('Failed to get output buffer'); @@ -397,6 +403,7 @@ public function doRequest(object $request): Response /** * Encodes the cookies and adds them to the headers. + * * @throws \yii\base\InvalidConfigException */ protected function encodeCookies( @@ -409,7 +416,9 @@ protected function encodeCookies( } foreach ($response->getCookies() as $cookie) { - /** @var \yii\web\Cookie $cookie */ + /** + * @var \yii\web\Cookie $cookie +*/ $value = $cookie->value; // Expire = 1 means we're removing the cookie if ($cookie->expire !== 1 && isset($validationKey)) { @@ -434,7 +443,8 @@ protected function encodeCookies( /** * Replace mailer with in memory mailer - * @param array $config Original configuration + * + * @param array $config Original configuration * @return array New configuration */ protected function mockMailer(array $config): array @@ -460,7 +470,7 @@ protected function mockMailer(array $config): array ]; if (isset($config['components'])) { - if (!is_array($config['components'])) { + if (! is_array($config['components'])) { throw new ModuleConfigException( $this, "Yii2 config does not contain components key is not of type array", @@ -491,13 +501,13 @@ public function restart(): void * Return an assoc array with the client context: cookieJar, history. * * @internal - * @return array{ cookieJar: CookieJar, history: History } + * @return array{ cookieJar: CookieJar, history: History } */ public function getContext(): array { return [ 'cookieJar' => $this->cookieJar, - 'history' => $this->history, + 'history' => $this->history, ]; } @@ -514,6 +524,7 @@ public function setContext(array $context): void /** * This functions closes the session of the application, if the application exists and has a session. + * * @internal */ public function closeSession(): void @@ -532,11 +543,10 @@ protected function resetResponse(Application $app): void { $method = $this->responseCleanMethod; // First check the current response object. - if ( - ($app->response->hasEventHandlers(YiiResponse::EVENT_BEFORE_SEND) - || $app->response->hasEventHandlers(YiiResponse::EVENT_AFTER_SEND) - || $app->response->hasEventHandlers(YiiResponse::EVENT_AFTER_PREPARE) - || count($app->response->getBehaviors()) > 0) + if (($app->response->hasEventHandlers(YiiResponse::EVENT_BEFORE_SEND) + || $app->response->hasEventHandlers(YiiResponse::EVENT_AFTER_SEND) + || $app->response->hasEventHandlers(YiiResponse::EVENT_AFTER_PREPARE) + || count($app->response->getBehaviors()) > 0) && $method === self::CLEAN_RECREATE ) { Debug::debug( @@ -608,7 +618,7 @@ protected function beforeRequest(): void $application = $this->getApplication(); - if (!$application instanceof Application) { + if (! $application instanceof Application) { throw new ConfigurationException('Application must be an instance of web application when doing requests'); } $this->resetResponse($application); diff --git a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php index 7f20d5c..b070d7e 100644 --- a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php +++ b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php @@ -13,13 +13,16 @@ /** * Class ConnectionWatcher * This class will watch for new database connection and store a reference to the connection object. + * * @package Codeception\Lib\Connector\Yii2 */ -class ConnectionWatcher +final class ConnectionWatcher { private Closure $handler; - /** @var Connection[] */ + /** + * @var Connection[] + */ private array $connections = []; public function __construct() @@ -59,8 +62,7 @@ public function closeAll(): void } /** - * @param string|array|JsonSerializable $message - * @return void + * @param string|array|JsonSerializable $message */ protected function debug(string|array|JsonSerializable $message): void { diff --git a/src/Codeception/Lib/Connector/Yii2/FixturesStore.php b/src/Codeception/Lib/Connector/Yii2/FixturesStore.php index 3852e69..d055c5a 100644 --- a/src/Codeception/Lib/Connector/Yii2/FixturesStore.php +++ b/src/Codeception/Lib/Connector/Yii2/FixturesStore.php @@ -7,7 +7,7 @@ use yii\test\FixtureTrait; use yii\test\InitDbFixture; -class FixturesStore +final class FixturesStore { use FixtureTrait; @@ -16,7 +16,10 @@ class FixturesStore * * FixturesStore constructor. */ - public function __construct(protected mixed $data) {} + public function __construct( + protected mixed $data + ) { + } public function fixtures(): mixed { diff --git a/src/Codeception/Lib/Connector/Yii2/Logger.php b/src/Codeception/Lib/Connector/Yii2/Logger.php index 928108a..69ebd4e 100644 --- a/src/Codeception/Lib/Connector/Yii2/Logger.php +++ b/src/Codeception/Lib/Connector/Yii2/Logger.php @@ -9,7 +9,7 @@ use yii\helpers\VarDumper; use yii\log\Logger as YiiLogger; -class Logger extends YiiLogger +final class Logger extends YiiLogger { /** * @var \SplQueue @@ -17,11 +17,12 @@ class Logger extends YiiLogger private \SplQueue $logQueue; /** - * @param int $maxLogItems * @param array $config */ - public function __construct(private readonly int $maxLogItems = 5, array $config = []) - { + public function __construct( + private readonly int $maxLogItems = 5, + array $config = [] + ) { parent::__construct($config); $this->logQueue = new \SplQueue(); } @@ -32,18 +33,21 @@ public function init(): void } /** - * @param string|array|YiiException $message + * @param string|array|YiiException $message * @param self::LEVEL_INFO|self::LEVEL_WARNING|self::LEVEL_ERROR $level - * @param string $category + * @param string $category */ public function log($message, $level, $category = 'application'): void { - if ( - !in_array($level, [ + if (! in_array( + $level, + [ self::LEVEL_INFO, self::LEVEL_WARNING, self::LEVEL_ERROR, - ], true) + ], + true + ) ) { return; } diff --git a/src/Codeception/Lib/Connector/Yii2/TestMailer.php b/src/Codeception/Lib/Connector/Yii2/TestMailer.php index 230e15a..89b9bca 100644 --- a/src/Codeception/Lib/Connector/Yii2/TestMailer.php +++ b/src/Codeception/Lib/Connector/Yii2/TestMailer.php @@ -7,7 +7,7 @@ use Closure; use yii\mail\BaseMailer; -class TestMailer extends BaseMailer +final class TestMailer extends BaseMailer { public $messageClass = \yii\symfonymailer\Message::class; diff --git a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php index cea3d01..ecfae4a 100644 --- a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php +++ b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php @@ -11,25 +11,29 @@ /** * Class TransactionForcer * This class adds support for forcing transactions as well as reusing PDO objects. + * * @package Codeception\Lib\Connector\Yii2 */ -class TransactionForcer extends ConnectionWatcher +final class TransactionForcer extends ConnectionWatcher { /** * @var array */ private array $pdoCache = []; + /** * @var array */ private array $dsnCache = []; + /** * @var array */ private array $transactions = []; - public function __construct(private bool $ignoreCollidingDSN) - { + public function __construct( + private bool $ignoreCollidingDSN + ) { parent::__construct(); } @@ -40,16 +44,20 @@ protected function connectionOpened(Connection $connection): void * We should check if the known PDO objects are the same, in which case we should reuse the PDO * object so only 1 transaction is started and multiple connections to the same database see the * same data (due to writes inside a transaction not being visible from the outside). - * */ - $key = md5(json_encode([ - 'dsn' => $connection->dsn, - 'user' => $connection->username, - 'pass' => $connection->password, - 'attributes' => $connection->attributes, - 'emulatePrepare' => $connection->emulatePrepare, - 'charset' => $connection->charset, - ], JSON_THROW_ON_ERROR)); + $key = md5( + json_encode( + [ + 'dsn' => $connection->dsn, + 'user' => $connection->username, + 'pass' => $connection->password, + 'attributes' => $connection->attributes, + 'emulatePrepare' => $connection->emulatePrepare, + 'charset' => $connection->charset, + ], + JSON_THROW_ON_ERROR + ) + ); /* * If keys match we assume connections are "similar enough". */ @@ -58,10 +66,9 @@ protected function connectionOpened(Connection $connection): void } elseif (isset($connection->pdo)) { $this->pdoCache[$key] = $connection->pdo; } - if ( - isset($this->dsnCache[$connection->dsn]) + if (isset($this->dsnCache[$connection->dsn]) && $this->dsnCache[$connection->dsn] !== $key - && !$this->ignoreCollidingDSN + && ! $this->ignoreCollidingDSN ) { $this->debug( <<transactions as $transaction) { if ($transaction->db->isActive) { $transaction->rollBack(); diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 8e8c37a..89a5a28 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -22,7 +22,6 @@ use Symfony\Component\BrowserKit\History; use Yii; use yii\base\Security; -use yii\web\Application as WebApplication; use yii\db\ActiveQueryInterface; use yii\db\ActiveRecordInterface; use yii\helpers\Url; @@ -30,6 +29,7 @@ use yii\mail\MessageInterface; use yii\test\Fixture; use yii\web\Application; +use yii\web\Application as WebApplication; use yii\web\IdentityInterface; /** @@ -218,7 +218,7 @@ * applicationClass: class-string<\yii\base\Application>|null * } */ -class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule +final class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule { /** * @var list @@ -227,16 +227,17 @@ class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule /** * Application config file must be set. + * * @var ModuleConfig */ protected array $config = [ 'fixturesMethod' => '_fixtures', - 'cleanup' => true, + 'cleanup' => true, 'ignoreCollidingDSN' => false, 'transaction' => true, 'configFile' => null, 'entryScript' => '', - 'entryUrl' => 'http://localhost/index-test.php', + 'entryUrl' => 'http://localhost/index-test.php', 'responseCleanMethod' => Yii2Connector::CLEAN_CLEAR, 'requestCleanMethod' => Yii2Connector::CLEAN_RECREATE, 'recreateComponents' => [], @@ -266,10 +267,10 @@ class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule private function getClient(): Yii2Connector { - if (!isset($this->client)) { + if (! isset($this->client)) { throw new RuntimeException('Browser not initialized'); } - if (!$this->client instanceof Yii2Connector) { + if (! $this->client instanceof Yii2Connector) { throw new RuntimeException('The Yii2 module must be used with the Yii2 browser client'); } return $this->client; @@ -311,13 +312,16 @@ private function initServerGlobal(): void $parsedUrl = parse_url($entryUrl); $entryFile = $this->config['entryScript'] ?: basename($entryUrl); $entryScript = $this->config['entryScript'] ?: ($parsedUrl['path'] ?? ''); - $_SERVER = array_merge($_SERVER, [ + $_SERVER = array_merge( + $_SERVER, + [ 'SCRIPT_FILENAME' => $entryFile, 'SCRIPT_NAME' => $entryScript, 'SERVER_NAME' => $parsedUrl['host'] ?? '', 'SERVER_PORT' => $parsedUrl['port'] ?? '80', 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https', - ]); + ] + ); } /** @@ -326,27 +330,27 @@ private function initServerGlobal(): void protected function validateConfig(): void { parent::validateConfig(); - if (!isset($this->config['configFile'])) { + if (! isset($this->config['configFile'])) { throw new ModuleConfigException( self::class, "The application config file was not configured", ); } $pathToConfig = codecept_absolute_path($this->config['configFile']); - if (!is_file($pathToConfig)) { + if (! is_file($pathToConfig)) { throw new ModuleConfigException( self::class, "The application config file does not exist: " . $pathToConfig, ); } $validMethods = implode(", ", Yii2Connector::CLEAN_METHODS); - if (!in_array($this->config['responseCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { + if (! in_array($this->config['responseCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { throw new ModuleConfigException( self::class, "The response clean method must be one of: " . $validMethods, ); } - if (!in_array($this->config['requestCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { + if (! in_array($this->config['requestCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { throw new ModuleConfigException( self::class, "The request clean method must be one of: " . $validMethods, @@ -354,7 +358,6 @@ protected function validateConfig(): void } } - /** * @param ClientConfig $settings */ @@ -379,13 +382,15 @@ protected function recreateClient(): void $parsedUrl = parse_url($entryUrl); $entryFile = $this->config['entryScript'] ?: basename($entryUrl); $entryScript = $this->config['entryScript'] ?: ($parsedUrl['path'] ?? ''); - $this->client = new Yii2Connector([ + $this->client = new Yii2Connector( + [ 'SCRIPT_FILENAME' => $entryFile, 'SCRIPT_NAME' => $entryScript, 'SERVER_NAME' => $parsedUrl['host'] ?? '', 'SERVER_PORT' => $parsedUrl['port'] ?? '80', 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https', - ]); + ] + ); $this->validateConfig(); $this->configureClient($this->config); } @@ -417,8 +422,7 @@ public function _before(TestInterface $test): void private function loadFixtures(object $test): void { $this->debugSection('Fixtures', 'Loading fixtures'); - if ( - $this->loadedFixtures === [] + if ($this->loadedFixtures === [] && method_exists($test, $this->config['fixturesMethod']) ) { $connectionWatcher = new ConnectionWatcher(); @@ -556,7 +560,7 @@ public function amLoggedInAs(int|string|IdentityInterface $user): void * ``` * instead of calling `haveFixtures` in Cest `_before` * - * @part fixtures + * @part fixtures * @param array $fixtures */ public function haveFixtures(array $fixtures): void @@ -574,7 +578,7 @@ public function haveFixtures(array $fixtures): void * Returns all loaded fixtures. * Array of fixture instances * - * @part fixtures + * @part fixtures * @return array */ public function grabFixtures(): array @@ -605,12 +609,12 @@ public function grabFixtures(): array * ``` * * @throws \Codeception\Exception\ModuleException if the fixture is not found - * @part fixtures + * @part fixtures */ public function grabFixture(string $name, null|string $index = null): Fixture|\yii\db\ActiveRecord|null { $fixtures = $this->grabFixtures(); - if (!isset($fixtures[$name])) { + if (! isset($fixtures[$name])) { throw new ModuleException($this, "Fixture $name is not loaded"); } $fixture = $fixtures[$name]; @@ -629,18 +633,21 @@ public function grabFixture(string $name, null|string $index = null): Fixture|\y * $user_id = $I->haveRecord('app\models\User', array('name' => 'Davert')); * ?> * ``` + * * @template T of \yii\db\ActiveRecord - * @param class-string $model - * @param array $attributes - * @part orm + * @param class-string $model + * @param array $attributes + * @part orm */ public function haveRecord(string $model, $attributes = []): mixed { - /** @var T $record */ + /** + * @var T $record +*/ $record = \Yii::createObject($model); $record->setAttributes($attributes, false); $res = $record->save(false); - if (!$res) { + if (! $res) { $this->fail("Record $model was not saved: " . \yii\helpers\Json::encode($record->errors)); } return $record->primaryKey; @@ -654,13 +661,13 @@ public function haveRecord(string $model, $attributes = []): mixed * ``` * * @param class-string<\yii\db\ActiveRecord> $model - * @param array $attributes - * @part orm + * @param array $attributes + * @part orm */ public function seeRecord(string $model, array $attributes = []): void { $record = $this->findRecord($model, $attributes); - if (!$record) { + if (! $record) { $this->fail("Couldn't find $model with " . json_encode($attributes)); } $this->debugSection($model, json_encode($record)); @@ -674,8 +681,8 @@ public function seeRecord(string $model, array $attributes = []): void * ``` * * @param class-string<\yii\db\ActiveRecord> $model - * @param array $attributes - * @part orm + * @param array $attributes + * @part orm */ public function dontSeeRecord(string $model, array $attributes = []): void { @@ -693,10 +700,10 @@ public function dontSeeRecord(string $model, array $attributes = []): void * $category = $I->grabRecord('app\models\User', array('name' => 'davert')); * ``` * - * @param class-string<\yii\db\ActiveRecord> $model - * @param array $attributes + * @param class-string<\yii\db\ActiveRecord> $model + * @param array $attributes * @return ActiveRecordInterface|null|array - * @part orm + * @part orm */ public function grabRecord(string $model, array $attributes = []): ActiveRecordInterface|null|array { @@ -704,19 +711,17 @@ public function grabRecord(string $model, array $attributes = []): ActiveRecordI } /** - * @param class-string<\yii\db\ActiveRecord> $model Class name - * @param array $attributes + * @param class-string<\yii\db\ActiveRecord> $model Class name + * @param array $attributes * @return ActiveRecordInterface|null|array */ protected function findRecord(string $model, array $attributes = []): ActiveRecordInterface|null|array { - if (!class_exists($model)) { + if (! class_exists($model)) { throw new RuntimeException("Class $model does not exist"); } $rc = new ReflectionClass($model); - if ( - $rc->hasMethod('find') - /** @phpstan-ignore-next-line */ + if ($rc->hasMethod('find') && ($findMethod = $rc->getMethod('find')) && $findMethod->isStatic() && $findMethod->isPublic() @@ -738,9 +743,9 @@ protected function findRecord(string $model, array $attributes = []): ActiveReco * $I->amOnRoute('site/view', ['page' => 'about']); * ``` * - * @param string $route A route + * @param string $route A route * @param array $params Additional route parameters - * @part route + * @part route */ public function amOnRoute(string $route, array $params = []): void { @@ -765,7 +770,7 @@ public function amOnRoute(string $route, array $params = []): void * ``` * * @throws \Codeception\Exception\ModuleException - * @part email + * @part email */ public function seeEmailIsSent(?int $num = null): void { @@ -798,7 +803,7 @@ public function dontSeeEmailIsSent(): void * $I->assertSame('admin@site,com', $messages[0]->getTo()); * ``` * - * @part email + * @part email * @return list List of sent emails * @throws \Codeception\Exception\ModuleException */ @@ -820,6 +825,7 @@ public function grabSentEmails(): array * $message = $I->grabLastSentEmail(); * $I->assertSame('admin@site,com', $message->getTo()); * ``` + * * @part email */ public function grabLastSentEmail(): BaseMessage|null @@ -832,6 +838,7 @@ public function grabLastSentEmail(): BaseMessage|null /** * Returns a list of regex patterns for recognized domain names + * * @return non-empty-list */ public function getInternalDomains(): array @@ -848,8 +855,9 @@ private function defineConstants(): void /** * Sets a cookie and, if validation is enabled, signs it. - * @param string $name The name of the cookie - * @param string $val The value of the cookie + * + * @param string $name The name of the cookie + * @param string $val The value of the cookie * @param array{domain?: string, path?: string, expires?: int, secure?:bool} $params Additional cookie params like `domain`, `path`, `expires` and `secure`. */ public function setCookie($name, $val, $params = []): void @@ -859,7 +867,8 @@ public function setCookie($name, $val, $params = []): void /** * Creates the CSRF Cookie. - * @param string $val The value of the CSRF token + * + * @param string $val The value of the CSRF token * @return string[] Returns an array containing the name of the CSRF param and the masked CSRF token. */ public function createAndSetCsrfCookie(string $val): array @@ -891,6 +900,7 @@ public function _initializeSession(): void /** * Return the session content for future restoring. Implements MultiSession. + * * @return SessionBackup */ public function _backupSession(): array @@ -909,6 +919,7 @@ public function _backupSession(): array /** * Restore a session. Implements MultiSession. + * * @param SessionBackup $session output of _backupSession() */ public function _loadSession($session): void @@ -926,11 +937,12 @@ public function _loadSession($session): void /** * Close and dump a session. Implements MultiSession. + * * @param mixed $session */ public function _closeSession($session = null): void { - if (!$session) { + if (! $session) { $this->_initializeSession(); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d6b21e6..9dcd5fa 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,5 +1,7 @@ 'Simple', 'basePath' => __DIR__, diff --git a/tests/cases/closeConnections/fixtures/EmptyFixture.php b/tests/cases/closeConnections/fixtures/EmptyFixture.php index 34bf498..688e5a5 100644 --- a/tests/cases/closeConnections/fixtures/EmptyFixture.php +++ b/tests/cases/closeConnections/fixtures/EmptyFixture.php @@ -6,9 +6,13 @@ use yii\test\DbFixture; -class EmptyFixture extends DbFixture +final class EmptyFixture extends DbFixture { - public function load() {} + public function load() + { + } - public function unload() {} + public function unload() + { + } } diff --git a/tests/cases/closeConnections/functional/FixturesCest.php b/tests/cases/closeConnections/functional/FixturesCest.php index a834b53..b8e7b82 100644 --- a/tests/cases/closeConnections/functional/FixturesCest.php +++ b/tests/cases/closeConnections/functional/FixturesCest.php @@ -9,7 +9,7 @@ use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class FixturesCest +final class FixturesCest { public function _fixtures() { @@ -26,12 +26,10 @@ protected function numberProvider() } /** - * @param FunctionalTester $I * @dataProvider numberProvider */ public function NoConnections(FunctionalTester $I, Example $example) { $I->assertSame(SqlliteHelper::connectionCount(), $example['count']); } - } diff --git a/tests/cases/closeConnections/functional/FixturesInBeforeCest.php b/tests/cases/closeConnections/functional/FixturesInBeforeCest.php index 3210b7b..72d33e1 100644 --- a/tests/cases/closeConnections/functional/FixturesInBeforeCest.php +++ b/tests/cases/closeConnections/functional/FixturesInBeforeCest.php @@ -9,7 +9,7 @@ use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class FixturesInBeforeCest +final class FixturesInBeforeCest { public function _before(FunctionalTester $I) { @@ -26,12 +26,10 @@ protected function numberProvider() } /** - * @param FunctionalTester $I * @dataProvider numberProvider */ public function NoConnections(FunctionalTester $I, Example $example) { $I->assertSame(SqlliteHelper::connectionCount(), $example['count']); } - } diff --git a/tests/cases/closeConnections/functional/NoFixturesCest.php b/tests/cases/closeConnections/functional/NoFixturesCest.php index 523336d..ba2e9bf 100644 --- a/tests/cases/closeConnections/functional/NoFixturesCest.php +++ b/tests/cases/closeConnections/functional/NoFixturesCest.php @@ -8,7 +8,7 @@ use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class NoFixturesCest +final class NoFixturesCest { protected function numberProvider() { @@ -16,7 +16,6 @@ protected function numberProvider() } /** - * @param FunctionalTester $I * @dataProvider numberProvider */ public function NoConnections(FunctionalTester $I, Example $example) diff --git a/tests/cases/closeConnections/helpers/SqlliteHelper.php b/tests/cases/closeConnections/helpers/SqlliteHelper.php index 70e662c..b7c4887 100644 --- a/tests/cases/closeConnections/helpers/SqlliteHelper.php +++ b/tests/cases/closeConnections/helpers/SqlliteHelper.php @@ -4,7 +4,7 @@ namespace tests\helpers; -class SqlliteHelper +final class SqlliteHelper { protected static $temp_name; @@ -30,5 +30,4 @@ public static function debug() codecept_debug("Executing : $cmd"); codecept_debug(shell_exec($cmd)); } - } diff --git a/tests/cases/closeConnectionsNoCleanup/config.php b/tests/cases/closeConnectionsNoCleanup/config.php index cf9fdb8..7c85f5f 100644 --- a/tests/cases/closeConnectionsNoCleanup/config.php +++ b/tests/cases/closeConnectionsNoCleanup/config.php @@ -1,5 +1,7 @@ 'Simple', 'basePath' => __DIR__, diff --git a/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php b/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php index 1fbe3e8..f9d9676 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/FixturesCest.php @@ -4,11 +4,11 @@ namespace tests\closeConnectionsNoCleanup; -use tests\FunctionalTester; use tests\fixtures\EmptyFixture; +use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class FixturesCest +final class FixturesCest { public function _fixtures() { @@ -36,5 +36,4 @@ public function NoConnections3(FunctionalTester $I) $count = SqlliteHelper::connectionCount(); $I->assertSame(0, $count); } - } diff --git a/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php b/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php index 3edc2c9..0329642 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/FixturesInBeforeCest.php @@ -4,11 +4,11 @@ namespace tests\closeConnectionsNoCleanup; -use tests\FunctionalTester; use tests\fixtures\EmptyFixture; +use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class FixturesInBeforeCest +final class FixturesInBeforeCest { public function _before(FunctionalTester $I) { diff --git a/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php b/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php index f109ab0..1df7ee1 100644 --- a/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php +++ b/tests/cases/closeConnectionsNoCleanup/functional/ThirdCest.php @@ -4,11 +4,11 @@ namespace tests\closeConnectionsNoCleanup; -use tests\FunctionalTester; use tests\fixtures\EmptyFixture; +use tests\FunctionalTester; use tests\helpers\SqlliteHelper; -class ThirdCest +final class ThirdCest { public function NoConnections1(FunctionalTester $I) { diff --git a/tests/cases/events/config.php b/tests/cases/events/config.php index 678f8db..31dce8c 100644 --- a/tests/cases/events/config.php +++ b/tests/cases/events/config.php @@ -1,5 +1,7 @@ 'Response Events', 'basePath' => __DIR__, diff --git a/tests/cases/events/controllers/SiteController.php b/tests/cases/events/controllers/SiteController.php index c5a90ce..02ef1a7 100644 --- a/tests/cases/events/controllers/SiteController.php +++ b/tests/cases/events/controllers/SiteController.php @@ -6,7 +6,9 @@ use yii\web\Controller; -class SiteController extends Controller +final class SiteController extends Controller { - public function actionIndex() {} + public function actionIndex() + { + } } diff --git a/tests/cases/events/functional/ResponseCest.php b/tests/cases/events/functional/ResponseCest.php index 2f57301..37176f9 100644 --- a/tests/cases/events/functional/ResponseCest.php +++ b/tests/cases/events/functional/ResponseCest.php @@ -7,7 +7,7 @@ use Codeception\Lib\Connector\Yii2; use yii\base\Event; -class ResponseCest +final class ResponseCest { public function testAfterSend(FunctionalTester $I) { diff --git a/tests/cases/locale-urls/config.php b/tests/cases/locale-urls/config.php index 9dae22c..0d3d76a 100644 --- a/tests/cases/locale-urls/config.php +++ b/tests/cases/locale-urls/config.php @@ -1,5 +1,7 @@ amOnRoute('/en/site/form'); $I->amOnRoute('/en/site/form'); } + public function testFormSubmit(FunctionalTester $I) { $I->amOnRoute('site/form'); @@ -38,5 +39,4 @@ public function testFormSubmit2(FunctionalTester $I) ]); $I->canSeeResponseCodeIs(201); } - } diff --git a/tests/cases/mock-mailer/config.php b/tests/cases/mock-mailer/config.php index 10a2778..a883b5f 100644 --- a/tests/cases/mock-mailer/config.php +++ b/tests/cases/mock-mailer/config.php @@ -1,5 +1,7 @@ 'Simple', 'basePath' => __DIR__, diff --git a/tests/cases/pageCacheHeaderAlreadySent/config.php b/tests/cases/pageCacheHeaderAlreadySent/config.php index b65fa02..830ecd1 100644 --- a/tests/cases/pageCacheHeaderAlreadySent/config.php +++ b/tests/cases/pageCacheHeaderAlreadySent/config.php @@ -1,5 +1,7 @@ 'PageCache', 'basePath' => __DIR__, diff --git a/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php b/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php index df2bf54..a9ea0f6 100644 --- a/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php +++ b/tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php @@ -6,7 +6,7 @@ use yii\filters\PageCache; -class UserController extends \yii\web\Controller +final class UserController extends \yii\web\Controller { public function behaviors() { diff --git a/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php b/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php index 43dcf3c..4fb68b6 100644 --- a/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php +++ b/tests/cases/pageCacheHeaderAlreadySent/functional/PageCest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -class PageCest +final class PageCest { public function testCache(\tests\FunctionalTester $I) { diff --git a/tests/cases/simple/config.php b/tests/cases/simple/config.php index 3f7d633..1c293dd 100644 --- a/tests/cases/simple/config.php +++ b/tests/cases/simple/config.php @@ -1,5 +1,7 @@ end(); } - /** * @param Action $action * @return bool diff --git a/tests/cases/simple/functional/SimpleCest.php b/tests/cases/simple/functional/SimpleCest.php index 0bb27b3..16e78fd 100644 --- a/tests/cases/simple/functional/SimpleCest.php +++ b/tests/cases/simple/functional/SimpleCest.php @@ -5,10 +5,9 @@ namespace tests; use Codeception\Exception\ModuleException; -use yii\base\ExitException; use yii\web\Application; -class SimpleCest +final class SimpleCest { public function testInstantiation(FunctionalTester $I) { diff --git a/tests/cases/simple/helpers/DummyUser.php b/tests/cases/simple/helpers/DummyUser.php index f3e9842..e78b9fe 100644 --- a/tests/cases/simple/helpers/DummyUser.php +++ b/tests/cases/simple/helpers/DummyUser.php @@ -6,7 +6,7 @@ use yii\web\IdentityInterface; -class DummyUser implements IdentityInterface +final class DummyUser implements IdentityInterface { /** * Finds an identity by the given ID. diff --git a/tests/cases/simple/helpers/EmptyString.php b/tests/cases/simple/helpers/EmptyString.php index 9cd51cf..c5792e8 100644 --- a/tests/cases/simple/helpers/EmptyString.php +++ b/tests/cases/simple/helpers/EmptyString.php @@ -7,7 +7,7 @@ /** * Class that is empty when converted to string */ -class EmptyString +final class EmptyString { private $value; diff --git a/tests/cases/sqlite/config.php b/tests/cases/sqlite/config.php index 363a298..d8d06c1 100644 --- a/tests/cases/sqlite/config.php +++ b/tests/cases/sqlite/config.php @@ -1,5 +1,7 @@ 'Simple', 'basePath' => __DIR__, diff --git a/tests/cases/sqlite/fixtures/TestFixture.php b/tests/cases/sqlite/fixtures/TestFixture.php index e781476..63cd0f9 100644 --- a/tests/cases/sqlite/fixtures/TestFixture.php +++ b/tests/cases/sqlite/fixtures/TestFixture.php @@ -5,12 +5,12 @@ namespace tests\fixtures; use yii\db\Connection; -use yii\db\Exception; use yii\test\DbFixture; -class TestFixture extends DbFixture +final class TestFixture extends DbFixture { public $tableName = 'test'; + public $tableConfig = [ 'id' => 'int', ]; diff --git a/tests/cases/sqlite/functional/SqLiteCest.php b/tests/cases/sqlite/functional/SqLiteCest.php index 732aefa..05fa0f7 100644 --- a/tests/cases/sqlite/functional/SqLiteCest.php +++ b/tests/cases/sqlite/functional/SqLiteCest.php @@ -7,7 +7,7 @@ use tests\fixtures\TestFixture; use yii\db\Connection; -class SqLiteCest +final class SqLiteCest { /** * This is called before the database transaction is started. diff --git a/tests/cases/yii2-app-advanced/config.php b/tests/cases/yii2-app-advanced/config.php index 7c2abde..72ebe3e 100644 --- a/tests/cases/yii2-app-advanced/config.php +++ b/tests/cases/yii2-app-advanced/config.php @@ -1,5 +1,7 @@ Date: Tue, 25 Feb 2025 11:52:12 +0100 Subject: [PATCH 05/17] chore(ci): add ecs to ci --- .github/workflows/main.yml | 2 ++ .gitignore | 3 +- ecs.php | 67 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 ecs.php diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7e7c9e4..abcb148 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,6 +28,8 @@ jobs: run: composer install --prefer-dist --no-progress --no-interaction --no-suggest - name: Run PHPStan run: php vendor/bin/phpstan + - name: Run ECS + run: php vendor/bin/ecs - name: Run test suite run: | diff --git a/.gitignore b/.gitignore index 151aca9..ff434cd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ tests/_support tests/_output tests/cases/yii2-app-advanced/_data/db.sqlite -.php-cs-fixer.cache \ No newline at end of file +.php-cs-fixer.cache +.ecs-cache diff --git a/ecs.php b/ecs.php new file mode 100644 index 0000000..5f8292b --- /dev/null +++ b/ecs.php @@ -0,0 +1,67 @@ +parallel(); + + $ecsConfig->cacheDirectory('.ecs-cache'); + // Paths + $ecsConfig->paths([ + __DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/ecs.php' + ]); + + // A. full sets + $ecsConfig->sets([SetList::PSR_12, SetList::SPACES]); + + $ecsConfig->rule(NotOperatorWithSuccessorSpaceFixer::class); + $ecsConfig->rule(ArraySyntaxFixer::class); + $ecsConfig->ruleWithConfiguration(GeneralPhpdocAnnotationRemoveFixer::class, [ + 'annotations' => ['author', 'inheritdoc'] + ]); + $ecsConfig->rule(NoBlankLinesAfterPhpdocFixer::class); + $ecsConfig->ruleWithConfiguration(NoSuperfluousPhpdocTagsFixer::class, [ + 'allow_mixed' => true + ]); + $ecsConfig->rule(NoEmptyPhpdocFixer::class); + $ecsConfig->rule(NoUnusedImportsFixer::class); + $ecsConfig->rule(DeclareStrictTypesFixer::class); + $ecsConfig->ruleWithConfiguration(FinalInternalClassFixer::class, [ + 'annotation_exclude' => ['@not-fix', '@internal'], + 'annotation_include' => [], + 'consider_absent_docblock_as_internal_class' => \true + ]); + $ecsConfig->ruleWithConfiguration(ForbiddenFunctionsSniff::class, [ + 'forbiddenFunctions' => [ + 'passthru' => null, + 'var_dump' => null, + ] + ]); + $ecsConfig->skip([ + ForbiddenFunctionsSniff::class => [ + 'tests/**', + 'console/**' + ] + ]); + + // $ecsConfig->skip([ + // FinalClassFixer::class => [ + // 'tests/**' + // ] + // ]); +}; From bf3c8010bd16f7d5904b251ef944c43a3b1f756a Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 25 Feb 2025 12:03:45 +0100 Subject: [PATCH 06/17] chore(sa): update stub file --- src/Codeception/Lib/Connector/Yii2.php | 13 ------- .../Lib/Connector/Yii2/ConnectionWatcher.php | 9 ++--- tests/Yii.stub | 39 +++++++++++++++++++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index 80b1b87..3820ab9 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -231,19 +231,6 @@ public function clearEmails(): void $this->emails = []; } - /** - * @internal - */ - public function getComponent(string $name): object|null - { - $app = $this->getApplication(); - $result = $app->get($name, false); - if (! isset($result)) { - throw new ConfigurationException("Component $name is not available in current application"); - } - return $result; - } - /** * Getting domain regex from rule host template */ diff --git a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php index b070d7e..e8d726a 100644 --- a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php +++ b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php @@ -13,15 +13,14 @@ /** * Class ConnectionWatcher * This class will watch for new database connection and store a reference to the connection object. - * - * @package Codeception\Lib\Connector\Yii2 + * @internal */ -final class ConnectionWatcher +class ConnectionWatcher { - private Closure $handler; + private readonly Closure $handler; /** - * @var Connection[] + * @var list */ private array $connections = []; diff --git a/tests/Yii.stub b/tests/Yii.stub index 2a715d2..11eaaae 100644 --- a/tests/Yii.stub +++ b/tests/Yii.stub @@ -33,6 +33,45 @@ namespace yii\web { * @return void */ public function setBaseUrl($url) {} + + /** + * @param array|object|null $params + */ + public function setBodyParams($params): void {} + + /** + * @param list|null $params + */ + public function setAcceptableContentTypes($params): void {} + /** + * @param list|null $params + */ + public function setAcceptableLanguages($params): void {} + + /** + * @param string|null $body + */ + public function setRawBody($body): void {} + + /** + * @param string|null $path + */ + public function setPathInfo($path): void {} + + /** + * @param string|null $path + */ + public function setScriptUrl($path): void {} + + /** + * @param string|null $path + */ + public function setUrl($path): void {} + + /** + * @param string|null $path + */ + public function setScriptFile($path): void {} } } From cd43a4e8450a03088a004e391a2de674466816db Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 25 Feb 2025 12:08:18 +0100 Subject: [PATCH 07/17] chore(sa): update baseline --- phpstan-baseline.neon | 78 ------------------------------------------- phpstan.neon | 3 -- 2 files changed, 81 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4417730..f462438 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1704,84 +1704,6 @@ parameters: count: 1 path: tests/cases/events/functional/ResponseCest.php - - - message: '#^Cannot call method getComponents\(\) on object\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/cases/locale-urls/config.php - - - - message: '#^Cannot call method has\(\) on object\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/cases/locale-urls/config.php - - - - message: '#^Cannot call method set\(\) on object\|null\.$#' - identifier: method.nonObject - count: 1 - path: tests/cases/locale-urls/config.php - - - - message: '#^Access to an undefined property yii\\console\\Request\|yii\\web\\Request\:\:\$bodyParams\.$#' - identifier: property.notFound - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Access to an undefined property yii\\console\\Response\|yii\\web\\Response\:\:\$statusCode\.$#' - identifier: property.notFound - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Cannot access property \$request on yii\\base\\Application\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Cannot access property \$response on yii\\base\\Application\|null\.$#' - identifier: property.nonObject - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Method app\\localeurls\\controllers\\SiteController\:\:actionForm\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Method app\\localeurls\\controllers\\SiteController\:\:actionPost\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/controllers/SiteController.php - - - - message: '#^Method tests\\LocaleUrlCest\:\:testFormSubmit\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/functional/LocaleUrlCest.php - - - - message: '#^Method tests\\LocaleUrlCest\:\:testFormSubmit2\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/functional/LocaleUrlCest.php - - - - message: '#^Method tests\\LocaleUrlCest\:\:testInstantiation\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/functional/LocaleUrlCest.php - - - - message: '#^Method tests\\LocaleUrlCest\:\:testMultipleGet\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/cases/locale-urls/functional/LocaleUrlCest.php - - message: '#^Cannot access property \$mailer on yii\\base\\Application\|null\.$#' identifier: property.nonObject diff --git a/phpstan.neon b/phpstan.neon index 974c4d0..e564cef 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -17,9 +17,6 @@ parameters: checkMaybeUndefinedVariables: true treatPhpDocTypesAsCertain: false ignoreErrors: - # All Yii setters accept `null` but their phpdoc is incorrect. -# - message: '~^Parameter #1 \$(.+) of method yii\\web\\Request::set(.+)\(\) expects (.+), null given.$~' -# path: 'src/' # If you want to ignore missing generics errors in the future, you can add: # - identifier: missingType.generics stubFiles: From 9afc4441ac9390bd26f43947b8c70bfc1081c04e Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 25 Feb 2025 15:52:27 +0100 Subject: [PATCH 08/17] chore(ci): add config for automated releases --- .github/workflows/{main.yml => prs.yml} | 9 +--- .github/workflows/release.yml | 71 +++++++++++++++++++++++++ .releaserc.json | 10 ++++ phpstan-baseline.neon | 6 --- tests/cases/sqlite/config.php | 6 +-- 5 files changed, 86 insertions(+), 16 deletions(-) rename .github/workflows/{main.yml => prs.yml} (96%) create mode 100644 .github/workflows/release.yml create mode 100644 .releaserc.json diff --git a/.github/workflows/main.yml b/.github/workflows/prs.yml similarity index 96% rename from .github/workflows/main.yml rename to .github/workflows/prs.yml index 4b7f626..0264bab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/prs.yml @@ -1,15 +1,12 @@ name: CI - -on: [push, pull_request] - +on: + pull_request: jobs: tests: runs-on: ubuntu-latest - strategy: matrix: php: [8.3, 8.4] - steps: - name: Checkout code uses: actions/checkout@v4 @@ -20,10 +17,8 @@ jobs: php-version: ${{ matrix.php }} extensions: pdo, sqlite, imagick coverage: none - - name: Validate composer.json and composer.lock run: composer validate - - name: Install dependencies run: composer install --prefer-dist --no-progress --no-interaction --no-suggest - name: Run ECS diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..43701aa --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,71 @@ +name: Automated release +on: + push: + branches: + - master +jobs: + static_analysis: + name: Static Analysis + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP with PECL extension + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + - uses: ramsey/composer-install@v3 + - name: Initialize cache + uses: actions/cache@v4 + with: + key: phpstan + path: .phpstan-cache + - name: Run PHPStan + run: vendor/bin/phpstan + - name: Run ECS + run: php vendor/bin/ecs + tests: + runs-on: ubuntu-latest + strategy: + matrix: + php: [8.3, 8.4] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: pdo, sqlite, imagick + coverage: none + - uses: ramsey/composer-install@v3 + - name: Run test suite + run: | + php vendor/bin/codecept build + php vendor/bin/codecept run + release: + name: Automated release + needs: + - static_analysis + - tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-node@v4 + with: + node-version: 22 + - run: > + npx + -p "@semantic-release/commit-analyzer" + -p "@semantic-release/release-notes-generator" + -p conventional-changelog-conventionalcommits + -p semantic-release + -- semantic-release + env: + GITHUB_TOKEN: ${{ secrets.COLLECTHOR_FINE_GRAINED_RELEASE }} +permissions: + packages: write + contents: write + pull-requests: write diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..a5c5e67 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,10 @@ +{ + "branches": ["master"], + "plugins": [ + ["@semantic-release/commit-analyzer", { + "preset": "conventionalcommits", + "presetConfig": {} + }], + "@semantic-release/github", + "@semantic-release/release-notes-generator"] +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f462438..ce4877d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1956,12 +1956,6 @@ parameters: count: 1 path: tests/cases/simple/helpers/EmptyString.php - - - message: '#^Parameter \#1 \$directory of function tempnam expects string, null given\.$#' - identifier: argument.type - count: 3 - path: tests/cases/sqlite/config.php - - message: '#^Cannot call method get\(\) on yii\\base\\Application\|null\.$#' identifier: method.nonObject diff --git a/tests/cases/sqlite/config.php b/tests/cases/sqlite/config.php index d8d06c1..0fc2b1b 100644 --- a/tests/cases/sqlite/config.php +++ b/tests/cases/sqlite/config.php @@ -9,15 +9,15 @@ 'components' => [ 'db' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . tempnam(null, '/file0'), + 'dsn' => 'sqlite:' . tempnam('', '/file0'), ], 'db1' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . tempnam(null, '/file1'), + 'dsn' => 'sqlite:' . tempnam('', '/file1'), ], 'db21' => [ 'class' => yii\db\Connection::class, - 'dsn' => 'sqlite:' . ($name = tempnam(null, '/file2')), + 'dsn' => 'sqlite:' . ($name = tempnam('', '/file2')), ], 'db22' => [ 'class' => yii\db\Connection::class, From e763dee616f1b75da68e8fe4a940ce8a99292ae2 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 25 Feb 2025 16:49:43 +0100 Subject: [PATCH 09/17] fix: remove unused dep --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 825d849..998784c 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,6 @@ "codeception/module-asserts": ">= 3.0", "codeception/module-filesystem": "> 3.0", "phpstan/phpstan": "^2", - "rector/rector": "^2", "symplify/easy-coding-standard": "^12.5" }, "autoload":{ From 68379632058e98dd589e7497ac10bdc235f06b77 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 11:43:02 +0100 Subject: [PATCH 10/17] chore: update code samples to use modern syntax --- src/Codeception/Module/Yii2.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 89a5a28..222660c 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -630,7 +630,7 @@ public function grabFixture(string $name, null|string $index = null): Fixture|\y * * ``` php * haveRecord('app\models\User', array('name' => 'Davert')); + * $user_id = $I->haveRecord(model: User::class, attributes: ['name' => 'Davert']); * ?> * ``` * @@ -657,7 +657,7 @@ public function haveRecord(string $model, $attributes = []): mixed * Checks that a record exists in the database. * * ```php - * $I->seeRecord('app\models\User', array('name' => 'davert')); + * $I->seeRecord(model: User::class, attributes: ['name' => 'davert']); * ``` * * @param class-string<\yii\db\ActiveRecord> $model @@ -677,7 +677,7 @@ public function seeRecord(string $model, array $attributes = []): void * Checks that a record does not exist in the database. * * ```php - * $I->dontSeeRecord('app\models\User', array('name' => 'davert')); + * $I->dontSeeRecord(User::class, attributes: ['name' => 'davert']); * ``` * * @param class-string<\yii\db\ActiveRecord> $model @@ -697,7 +697,7 @@ public function dontSeeRecord(string $model, array $attributes = []): void * Retrieves a record from the database * * ```php - * $category = $I->grabRecord('app\models\User', array('name' => 'davert')); + * $category = $I->grabRecord(User::class, attributes: ['name' => 'davert']); * ``` * * @param class-string<\yii\db\ActiveRecord> $model From 8366c51b0eaca44fb209e852e02cdc2931a56751 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 11:43:43 +0100 Subject: [PATCH 11/17] chore(cs): use stricter rule set --- ecs.php | 5 ++--- src/Codeception/Lib/Connector/Yii2.php | 3 +-- tests/cases/sqlite/fixtures/TestFixture.php | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ecs.php b/ecs.php index 5f8292b..ed2987a 100644 --- a/ecs.php +++ b/ecs.php @@ -27,7 +27,7 @@ ]); // A. full sets - $ecsConfig->sets([SetList::PSR_12, SetList::SPACES]); + $ecsConfig->sets([SetList::PSR_12, SetList::SPACES, SetList::STRICT]); $ecsConfig->rule(NotOperatorWithSuccessorSpaceFixer::class); $ecsConfig->rule(ArraySyntaxFixer::class); @@ -40,11 +40,10 @@ ]); $ecsConfig->rule(NoEmptyPhpdocFixer::class); $ecsConfig->rule(NoUnusedImportsFixer::class); - $ecsConfig->rule(DeclareStrictTypesFixer::class); $ecsConfig->ruleWithConfiguration(FinalInternalClassFixer::class, [ 'annotation_exclude' => ['@not-fix', '@internal'], 'annotation_include' => [], - 'consider_absent_docblock_as_internal_class' => \true + 'consider_absent_docblock_as_internal_class' => true ]); $ecsConfig->ruleWithConfiguration(ForbiddenFunctionsSniff::class, [ 'forbiddenFunctions' => [ diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index 3820ab9..b822c92 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -17,7 +17,6 @@ use Symfony\Component\BrowserKit\Request as BrowserkitRequest; use Symfony\Component\BrowserKit\Response; use Yii; -use yii\base\Component; use yii\base\Event; use yii\base\ExitException; use yii\base\Security; @@ -25,13 +24,13 @@ use yii\mail\BaseMessage; use yii\web\Application; use yii\web\IdentityInterface; -use yii\web\Request; use yii\web\Request as YiiRequest; use yii\web\Response as YiiResponse; use yii\web\User; /** * @extends Client + * @internal This class is not part of the public API */ final class Yii2 extends Client { diff --git a/tests/cases/sqlite/fixtures/TestFixture.php b/tests/cases/sqlite/fixtures/TestFixture.php index 63cd0f9..dd32324 100644 --- a/tests/cases/sqlite/fixtures/TestFixture.php +++ b/tests/cases/sqlite/fixtures/TestFixture.php @@ -31,7 +31,7 @@ public function unload() foreach ($this->dbComponents as $name) { /** @var Connection $connection */ $connection = \Yii::$app->get($name); - if (in_array($this->tableName, $connection->getSchema()->getTableNames('', true))) { + if (in_array($this->tableName, $connection->getSchema()->getTableNames('', true), true)) { $connection->createCommand()->dropTable($this->tableName)->execute(); } } From 97610ed08b341ab33c37ba4b9d49409eaef51438 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 11:49:12 +0100 Subject: [PATCH 12/17] fix(cs): indent in phpdoc --- ecs.php | 3 ++- src/Codeception/Lib/Connector/Yii2.php | 8 ++++---- src/Codeception/Lib/Connector/Yii2/TransactionForcer.php | 4 ++-- src/Codeception/Module/Yii2.php | 9 +++++---- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ecs.php b/ecs.php index ed2987a..9c87c5c 100644 --- a/ecs.php +++ b/ecs.php @@ -12,7 +12,7 @@ use PhpCsFixer\Fixer\Phpdoc\NoBlankLinesAfterPhpdocFixer; use PhpCsFixer\Fixer\Phpdoc\NoEmptyPhpdocFixer; use PhpCsFixer\Fixer\Phpdoc\NoSuperfluousPhpdocTagsFixer; -use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer; +use PhpCsFixer\Fixer\Phpdoc\PhpdocIndentFixer; use Symplify\EasyCodingStandard\Config\ECSConfig; use Symplify\EasyCodingStandard\ValueObject\Set\SetList; @@ -51,6 +51,7 @@ 'var_dump' => null, ] ]); + $ecsConfig->rule(PhpdocIndentFixer::class); $ecsConfig->skip([ ForbiddenFunctionsSniff::class => [ 'tests/**', diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index b822c92..bd4b180 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -201,8 +201,8 @@ public function getInternalDomains(): array if ($urlManager->enablePrettyUrl) { foreach ($urlManager->rules as $rule) { /** - * @var \yii\web\UrlRule $rule -*/ + * @var \yii\web\UrlRule $rule + */ if ($rule->host !== null) { $domains[] = $this->getDomainRegex($rule->host); } @@ -403,8 +403,8 @@ protected function encodeCookies( foreach ($response->getCookies() as $cookie) { /** - * @var \yii\web\Cookie $cookie -*/ + * @var \yii\web\Cookie $cookie + */ $value = $cookie->value; // Expire = 1 means we're removing the cookie if ($cookie->expire !== 1 && isset($validationKey)) { diff --git a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php index ecfae4a..60cd627 100644 --- a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php +++ b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php @@ -91,8 +91,8 @@ protected function connectionOpened(Connection $connection): void public function rollbackAll(): void { /** - * @var Transaction $transaction -*/ + * @var Transaction $transaction + */ foreach ($this->transactions as $transaction) { if ($transaction->db->isActive) { $transaction->rollBack(); diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 222660c..df41499 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -636,14 +636,15 @@ public function grabFixture(string $name, null|string $index = null): Fixture|\y * * @template T of \yii\db\ActiveRecord * @param class-string $model - * @param array $attributes + * @param array $attributes * @part orm + * @return int|string|array The primary key */ - public function haveRecord(string $model, $attributes = []): mixed + public function haveRecord(string $model, $attributes = []): int|string|array { /** - * @var T $record -*/ + * @var T $record + */ $record = \Yii::createObject($model); $record->setAttributes($attributes, false); $res = $record->save(false); From 0d981258ef962f1bd3cbe948bba29221631a272f Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 11:54:36 +0100 Subject: [PATCH 13/17] fix(cs): add rules for docblocks --- ecs.php | 6 ++++-- .../Lib/Connector/Yii2/ConnectionWatcher.php | 1 - src/Codeception/Lib/Connector/Yii2/FixturesStore.php | 2 -- .../Lib/Connector/Yii2/TransactionForcer.php | 3 --- src/Codeception/Module/Yii2.php | 2 +- tests/cases/simple/helpers/DummyUser.php | 10 +++++----- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/ecs.php b/ecs.php index 9c87c5c..0a1526b 100644 --- a/ecs.php +++ b/ecs.php @@ -27,12 +27,12 @@ ]); // A. full sets - $ecsConfig->sets([SetList::PSR_12, SetList::SPACES, SetList::STRICT]); + $ecsConfig->sets([SetList::PSR_12, SetList::SPACES, SetList::STRICT, SetList::DOCBLOCK]); $ecsConfig->rule(NotOperatorWithSuccessorSpaceFixer::class); $ecsConfig->rule(ArraySyntaxFixer::class); $ecsConfig->ruleWithConfiguration(GeneralPhpdocAnnotationRemoveFixer::class, [ - 'annotations' => ['author', 'inheritdoc'] + 'annotations' => ['author', 'inheritdoc', 'package'] ]); $ecsConfig->rule(NoBlankLinesAfterPhpdocFixer::class); $ecsConfig->ruleWithConfiguration(NoSuperfluousPhpdocTagsFixer::class, [ @@ -52,6 +52,8 @@ ] ]); $ecsConfig->rule(PhpdocIndentFixer::class); + $ecsConfig->rule(\PhpCsFixer\Fixer\Phpdoc\AlignMultilineCommentFixer::class); + $ecsConfig->skip([ ForbiddenFunctionsSniff::class => [ 'tests/**', diff --git a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php index e8d726a..e3be083 100644 --- a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php +++ b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php @@ -11,7 +11,6 @@ use yii\db\Connection; /** - * Class ConnectionWatcher * This class will watch for new database connection and store a reference to the connection object. * @internal */ diff --git a/src/Codeception/Lib/Connector/Yii2/FixturesStore.php b/src/Codeception/Lib/Connector/Yii2/FixturesStore.php index d055c5a..d985bd3 100644 --- a/src/Codeception/Lib/Connector/Yii2/FixturesStore.php +++ b/src/Codeception/Lib/Connector/Yii2/FixturesStore.php @@ -13,8 +13,6 @@ final class FixturesStore /** * Expects fixtures config - * - * FixturesStore constructor. */ public function __construct( protected mixed $data diff --git a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php index 60cd627..e93ac44 100644 --- a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php +++ b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php @@ -9,10 +9,7 @@ use yii\db\Transaction; /** - * Class TransactionForcer * This class adds support for forcing transactions as well as reusing PDO objects. - * - * @package Codeception\Lib\Connector\Yii2 */ final class TransactionForcer extends ConnectionWatcher { diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index df41499..4d81f6a 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -257,7 +257,7 @@ final class Yii2 extends Framework implements ActiveRecord, MultiSession, Parted private TransactionForcer $transactionForcer; /** - * @var array The contents of $_SERVER upon initialization of this object. + * @var array The contents of upon initialization of this object. * This is only used to restore it upon object destruction. * It MUST not be used anywhere else. */ diff --git a/tests/cases/simple/helpers/DummyUser.php b/tests/cases/simple/helpers/DummyUser.php index e78b9fe..c7ec457 100644 --- a/tests/cases/simple/helpers/DummyUser.php +++ b/tests/cases/simple/helpers/DummyUser.php @@ -18,7 +18,7 @@ final class DummyUser implements IdentityInterface public static function findIdentity($id) { return null; - // TODO: Implement findIdentity() method. + } /** @@ -32,7 +32,7 @@ public static function findIdentity($id) */ public static function findIdentityByAccessToken($token, $type = null) { - // TODO: Implement findIdentityByAccessToken() method. + } /** @@ -41,7 +41,7 @@ public static function findIdentityByAccessToken($token, $type = null) */ public function getId() { - // TODO: Implement getId() method. + } /** @@ -58,7 +58,7 @@ public function getId() */ public function getAuthKey() { - // TODO: Implement getAuthKey() method. + } /** @@ -71,6 +71,6 @@ public function getAuthKey() */ public function validateAuthKey($authKey) { - // TODO: Implement validateAuthKey() method. + } } From 785dea7b1e397d72f9b6347c1461c125e575d4e2 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 13:47:35 +0100 Subject: [PATCH 14/17] chore(cs): fix static analysis by improving stub --- src/Codeception/Module/Yii2.php | 6 ++---- tests/Yii.stub | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 4d81f6a..06fc57f 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -28,7 +28,6 @@ use yii\mail\BaseMessage; use yii\mail\MessageInterface; use yii\test\Fixture; -use yii\web\Application; use yii\web\Application as WebApplication; use yii\web\IdentityInterface; @@ -647,11 +646,10 @@ public function haveRecord(string $model, $attributes = []): int|string|array */ $record = \Yii::createObject($model); $record->setAttributes($attributes, false); - $res = $record->save(false); - if (! $res) { + if (! $record->save(false)) { $this->fail("Record $model was not saved: " . \yii\helpers\Json::encode($record->errors)); } - return $record->primaryKey; + return $record->getPrimaryKey(); } /** diff --git a/tests/Yii.stub b/tests/Yii.stub index 11eaaae..bbfd44a 100644 --- a/tests/Yii.stub +++ b/tests/Yii.stub @@ -17,6 +17,27 @@ namespace yii { namespace yii\base { class Application {} + + class Model {} +} + +namespace yii\db { + class BaseActiveRecord extends \yii\base\Model { + /** + * @param bool $asArray + * @return int|string|array|null + */ + public function getPrimaryKey($asArray = false) {} + + /** + * @return bool + * @param bool $runValidation + * @param null|list $attributeNames + * @phpstan-assert-if-true (int|string|array) $this->getPrimaryKey() + */ + public function save($runValidation = true, $attributeNames = null) {} + + } } namespace yii\web { From 117117e6cb6c4b5030b4072c5de34660973a1661 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 14:03:38 +0100 Subject: [PATCH 15/17] fix(sa): use Assert::fail so that phpstan understand code doesn't continue --- phpstan.neon | 3 +++ src/Codeception/Module/Yii2.php | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index e564cef..fb2d525 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -17,6 +17,9 @@ parameters: checkMaybeUndefinedVariables: true treatPhpDocTypesAsCertain: false ignoreErrors: + - identifier: return.type + path: tests/_support/_generated/FunctionalTesterActions.php + message: "# but returns mixed.$#" # If you want to ignore missing generics errors in the future, you can add: # - identifier: missingType.generics stubFiles: diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 06fc57f..768c69a 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -16,6 +16,7 @@ use Codeception\Lib\Interfaces\MultiSession; use Codeception\Lib\Interfaces\PartedModule; use Codeception\TestInterface; +use PHPUnit\Framework\Assert; use ReflectionClass; use RuntimeException; use Symfony\Component\BrowserKit\CookieJar; @@ -647,7 +648,7 @@ public function haveRecord(string $model, $attributes = []): int|string|array $record = \Yii::createObject($model); $record->setAttributes($attributes, false); if (! $record->save(false)) { - $this->fail("Record $model was not saved: " . \yii\helpers\Json::encode($record->errors)); + Assert::fail("Record $model was not saved: " . \yii\helpers\Json::encode($record->errors)); } return $record->getPrimaryKey(); } @@ -667,7 +668,7 @@ public function seeRecord(string $model, array $attributes = []): void { $record = $this->findRecord($model, $attributes); if (! $record) { - $this->fail("Couldn't find $model with " . json_encode($attributes)); + Assert::fail("Couldn't find $model with " . json_encode($attributes)); } $this->debugSection($model, json_encode($record)); } @@ -688,7 +689,7 @@ public function dontSeeRecord(string $model, array $attributes = []): void $record = $this->findRecord($model, $attributes); $this->debugSection($model, json_encode($record)); if ($record) { - $this->fail("Unexpectedly managed to find $model with " . json_encode($attributes)); + Assert::fail("Unexpectedly managed to find $model with " . json_encode($attributes)); } } From e045ef59bff449c4ba92ee5ba4eb2a670b5f6b4f Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 14:07:42 +0100 Subject: [PATCH 16/17] fix(ci): github token name --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43701aa..aa05917 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,7 +64,7 @@ jobs: -p semantic-release -- semantic-release env: - GITHUB_TOKEN: ${{ secrets.COLLECTHOR_FINE_GRAINED_RELEASE }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} permissions: packages: write contents: write From d8111f00cb5e1fe5395f0b255e53ff5eb31bba56 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 14:08:54 +0100 Subject: [PATCH 17/17] fix(ci): use dry run for automated release --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aa05917..7439044 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,7 +62,7 @@ jobs: -p "@semantic-release/release-notes-generator" -p conventional-changelog-conventionalcommits -p semantic-release - -- semantic-release + -- semantic-release --dry-run env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} permissions: