From dca7c446877d1e4143db9deb86a499df40af59ef Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 10:18:00 +0100 Subject: [PATCH 01/21] feat: allow mails to be intercepted using events --- src/Codeception/Lib/Connector/Yii2.php | 30 ++++++++++++++++++- .../Lib/Connector/Yii2/TestMailer.php | 3 +- src/Codeception/Module/Yii2.php | 12 ++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index b7df1d5..c6cc874 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -18,6 +18,9 @@ use yii\base\ExitException; use yii\base\Security; use yii\base\UserException; +use yii\mail\BaseMailer; +use yii\mail\MailerInterface; +use yii\mail\MailEvent; use yii\mail\MessageInterface; use yii\web\Application; use yii\web\ErrorHandler; @@ -30,6 +33,19 @@ class Yii2 extends Client { use Shared\PhpSuperGlobalsConverter; + const MAIL_METHODS = [ + self::MAIL_CATCH, + self::MAIL_EVENT_AFTER, + self::MAIL_EVENT_BEFORE, + self::MAIL_IGNORE + ]; + + public const MAIL_CATCH = 'catch'; + public const MAIL_EVENT_AFTER = 'after'; + public const MAIL_EVENT_BEFORE = 'before'; + public const MAIL_IGNORE = 'ignore'; + + const CLEAN_METHODS = [ self::CLEAN_RECREATE, self::CLEAN_CLEAR, @@ -64,6 +80,10 @@ class Yii2 extends Client */ public $configFile; + /** + * @var self::MAIL_CATCH|self::MAIL_IGNORE|self::MAIL_AFTER|self::MAIL_BEFORE $mailMethod method for handling mails + */ + public $mailMethod; /** * @var string method for cleaning the response object before each request */ @@ -267,7 +287,15 @@ public function startApp(?\yii\log\Logger $logger = null): void unset($config['container']); } - $config = $this->mockMailer($config); + match ($this->mailMethod) { + self::MAIL_CATCH => $config= $this->mockMailer($config), + self::MAIL_EVENT_AFTER => $config['components']['mailer']['on ' . BaseMailer::EVENT_AFTER_SEND] = fn(MailEvent $event) => $this->emails[] = $event->message, + self::MAIL_EVENT_BEFORE => $config['components']['mailer']['on ' . BaseMailer::EVENT_BEFORE_SEND] = function(MailEvent $event) { + $this->emails[] = $event->message; + return true; + }, + self::MAIL_IGNORE => null// Do nothing + } Yii::$app = Yii::createObject($config); if ($logger instanceof \yii\log\Logger) { diff --git a/src/Codeception/Lib/Connector/Yii2/TestMailer.php b/src/Codeception/Lib/Connector/Yii2/TestMailer.php index 230e15a..69d51f4 100644 --- a/src/Codeception/Lib/Connector/Yii2/TestMailer.php +++ b/src/Codeception/Lib/Connector/Yii2/TestMailer.php @@ -6,10 +6,11 @@ use Closure; use yii\mail\BaseMailer; +use yii\symfonymailer\Message; class TestMailer extends BaseMailer { - public $messageClass = \yii\symfonymailer\Message::class; + public $messageClass = Message::class; public Closure $callback; diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index fdd47b8..2b62aef 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -86,6 +86,11 @@ * changes will get discarded. * * `recreateApplication` - (default: `false`) whether to recreate the whole * application before each request + * * `mailMethod` - (default: `catch`) Method for handling email via the 'mailer' + * component. `ignore` will not do anything with mail, this means mails are not + * inspectable by the test runner, using `before` or `after` will use mailer + * events; making the mails inspectable but also allowing your default mail + * handling to work * * You can use this module by setting params in your `functional.suite.yml`: * @@ -187,6 +192,7 @@ class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule 'requestCleanMethod' => Yii2Connector::CLEAN_RECREATE, 'recreateComponents' => [], 'recreateApplication' => false, + 'mailMethod' => Yii2Connector::MAIL_CATCH, 'closeSessionOnRecreateApplication' => true, 'applicationClass' => null, ]; @@ -289,6 +295,12 @@ protected function validateConfig(): void "The response clean method must be one of: " . $validMethods ); } + if (!in_array($this->config['mailMethod'], Yii2Connector::MAIL_METHODS, true)) { + throw new ModuleConfigException( + self::class, + "The mail method must be one of: " . $validMethods + ); + } if (!in_array($this->config['requestCleanMethod'], Yii2Connector::CLEAN_METHODS, true)) { throw new ModuleConfigException( self::class, From 5bcf28ea3bd500e595f37be5d01757d45017f0ff Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 10:22:34 +0100 Subject: [PATCH 02/21] chore: add missing ; --- src/Codeception/Lib/Connector/Yii2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index c6cc874..45001e0 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -295,7 +295,7 @@ public function startApp(?\yii\log\Logger $logger = null): void return true; }, self::MAIL_IGNORE => null// Do nothing - } + }; Yii::$app = Yii::createObject($config); if ($logger instanceof \yii\log\Logger) { From 907f28016a667204df7e4da73f9fc00ffd2204d9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 16:32:21 +0100 Subject: [PATCH 03/21] chore: fix merge mistake --- src/Codeception/Lib/Connector/Yii2.php | 12 ++++++------ src/Codeception/Module/Yii2.php | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index d1a6f4a..9616b99 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -15,20 +15,16 @@ 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; use yii\base\Event; use yii\base\ExitException; use yii\base\Security; use yii\base\UserException; use yii\mail\BaseMessage; -use yii\mail\MailEvent; 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; @@ -308,7 +304,11 @@ public function startApp(?\yii\log\Logger $logger = null): void } $config = $this->mockMailer($config); - Yii::$app = Yii::createObject($config); + $app = Yii::createObject($config); + if (!$app instanceof \yii\base\Application) { + throw new ModuleConfigException($this, "Failed to initialize Yii2 app"); + } + \Yii::$app = $app; if ($logger instanceof \yii\log\Logger) { Yii::setLogger($logger); diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index fb6a585..30f6403 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -22,14 +22,13 @@ 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; 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; /** From 93c851db96d97942893322ed6f04769f3e22cc7e Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 16:58:04 +0100 Subject: [PATCH 04/21] chore: cleanup --- src/Codeception/Lib/Connector/Yii2.php | 53 +++++++++++++++----------- src/Codeception/Module/Yii2.php | 20 +++------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index 9616b99..864a92d 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -21,7 +21,9 @@ use yii\base\ExitException; use yii\base\Security; use yii\base\UserException; -use yii\mail\BaseMessage; +use yii\mail\BaseMailer; +use yii\mail\MailEvent; +use yii\mail\MessageInterface; use yii\web\Application; use yii\web\IdentityInterface; use yii\web\Request as YiiRequest; @@ -36,20 +38,21 @@ class Yii2 extends Client { use Shared\PhpSuperGlobalsConverter; - const MAIL_METHODS = [ + + public const array MAIL_METHODS = [ self::MAIL_CATCH, self::MAIL_EVENT_AFTER, self::MAIL_EVENT_BEFORE, self::MAIL_IGNORE ]; - public const MAIL_CATCH = 'catch'; - public const MAIL_EVENT_AFTER = 'after'; - public const MAIL_EVENT_BEFORE = 'before'; - public const MAIL_IGNORE = 'ignore'; + public const string MAIL_CATCH = 'catch'; + public const string MAIL_EVENT_AFTER = 'after'; + public const string MAIL_EVENT_BEFORE = 'before'; + public const string MAIL_IGNORE = 'ignore'; - const CLEAN_METHODS = [ + const array CLEAN_METHODS = [ self::CLEAN_RECREATE, self::CLEAN_CLEAR, self::CLEAN_FORCE_RECREATE, @@ -59,55 +62,55 @@ class Yii2 extends Client * 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'; + const string CLEAN_RECREATE = 'recreate'; /** * Same as recreate but will not warn when behaviors / event handlers are lost. */ - const CLEAN_FORCE_RECREATE = 'force_recreate'; + const string 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'; + const string 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'; + const string CLEAN_MANUAL = 'manual'; /** * @var string application config file */ - public $configFile; + public string $configFile; /** - * @var self::MAIL_CATCH|self::MAIL_IGNORE|self::MAIL_AFTER|self::MAIL_BEFORE $mailMethod method for handling mails + * @var self::MAIL_CATCH|self::MAIL_IGNORE|self::MAIL_EVENT_AFTER|self::MAIL_EVENT_BEFORE method for handling mails */ - public $mailMethod; + public string $mailMethod; /** * @var string method for cleaning the response object before each request */ - public $responseCleanMethod; + public string $responseCleanMethod; /** * @var string method for cleaning the request object before each request */ - public $requestCleanMethod; + public string $requestCleanMethod; /** * @var string[] List of component names that must be recreated before each request */ - public $recreateComponents = []; + public array $recreateComponents = []; /** * 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; + public bool $recreateApplication = false; /** * @var bool whether to close the session in between requests inside a single test, if recreateApplication is set to true @@ -122,7 +125,7 @@ class Yii2 extends Client /** - * @var list + * @var list */ private array $emails = []; @@ -224,7 +227,7 @@ public function getInternalDomains(): array /** * @internal - * @return list List of sent emails + * @return list List of sent emails */ public function getEmails(): array { @@ -303,7 +306,13 @@ public function startApp(?\yii\log\Logger $logger = null): void unset($config['container']); } - $config = $this->mockMailer($config); + match ($this->mailMethod) { + self::MAIL_CATCH => $config= $this->mockMailer($config), + self::MAIL_EVENT_AFTER => $config['components']['mailer']['on ' . BaseMailer::EVENT_AFTER_SEND] = fn(MailEvent $event) => $this->emails[] = $event->message, + self::MAIL_EVENT_BEFORE => $config['components']['mailer']['on ' . BaseMailer::EVENT_BEFORE_SEND] = fn(MailEvent $event) => $this->emails[] = $event->message, + self::MAIL_IGNORE => null// Do nothing + }; + $app = Yii::createObject($config); if (!$app instanceof \yii\base\Application) { throw new ModuleConfigException($this, "Failed to initialize Yii2 app"); @@ -468,7 +477,7 @@ protected function mockMailer(array $config): array $mailerConfig = [ 'class' => TestMailer::class, - 'callback' => function (BaseMessage $message): void { + 'callback' => function (MessageInterface $message): void { $this->emails[] = $message; } ]; diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 30f6403..cc50c8a 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -193,24 +193,14 @@ * recreateComponents: list, * recreateApplication: bool, * closeSessionOnRecreateApplication: bool, + * mailMethod: Yii2Connector::MAIL_CATCH|Yii2Connector::MAIL_IGNORE|Yii2Connector::MAIL_EVENT_AFTER|Yii2Connector::MAIL_EVENT_BEFORE, * applicationClass: class-string<\yii\base\Application>|null * } * - * @phpstan-type ValidConfig array{ - * fixturesMethod: string, - * cleanup: bool, - * ignoreCollidingDSN: bool, + * @phpstan-type ValidConfig (ModuleConfig & array{ * transaction: bool|null, - * entryScript: string, - * entryUrl: string, * configFile: string, - * responseCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, - * requestCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, - * recreateComponents: list, - * recreateApplication: bool, - * closeSessionOnRecreateApplication: bool, - * applicationClass: class-string<\yii\base\Application>|null - * } + * }) * @phpstan-type SessionBackup array{cookie: array, session: array, headers: array, clientContext: array{ cookieJar: CookieJar, history: History }} * @phpstan-type ClientConfig array{ * configFile: string, @@ -808,7 +798,7 @@ public function dontSeeEmailIsSent(): void * ``` * * @part email - * @return list List of sent emails + * @return list List of sent emails * @throws \Codeception\Exception\ModuleException */ public function grabSentEmails(): array @@ -831,7 +821,7 @@ public function grabSentEmails(): array * ``` * @part email */ - public function grabLastSentEmail(): BaseMessage|null + public function grabLastSentEmail(): MessageInterface|null { $this->seeEmailIsSent(); $messages = $this->grabSentEmails(); From d53611637e9ce5291bd13ea1a5704f60828cd12c Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 17:22:11 +0100 Subject: [PATCH 05/21] fix: load mailmethod config --- src/Codeception/Module/Yii2.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index cc50c8a..ec05487 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -368,6 +368,7 @@ private function configureClient(array $settings): void $client->recreateApplication = $settings['recreateApplication']; $client->closeSessionOnRecreateApplication = $settings['closeSessionOnRecreateApplication']; $client->applicationClass = $settings['applicationClass']; + $client->mailMethod = $settings['mailMethod']; $client->resetApplication(); } From ab5ccc555c1907b7d19253a6435433f35bb76fe5 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Mon, 24 Feb 2025 17:46:00 +0100 Subject: [PATCH 06/21] fix: phpstan types --- src/Codeception/Module/Yii2.php | 57 ++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index ec05487..a4eefb4 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -179,38 +179,43 @@ * * Maintainer: **samdark** * Stability: **stable** - * + * @phpstan-type ClientConfig array{ + * responseCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, + * requestCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, + * mailMethod: Yii2Connector::MAIL_CATCH|Yii2Connector::MAIL_IGNORE|Yii2Connector::MAIL_EVENT_AFTER|Yii2Connector::MAIL_EVENT_BEFORE, + * recreateComponents: list, + * recreateApplication: bool, + * closeSessionOnRecreateApplication: bool, + * applicationClass: class-string<\yii\base\Application>|null, + * configFile: string + * } * @phpstan-type ModuleConfig array{ - * fixturesMethod: string, - * cleanup: bool, - * ignoreCollidingDSN: bool, - * transaction: bool|null, - * entryScript: string, - * entryUrl: string, - * configFile: string|null, - * responseCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, - * requestCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, - * recreateComponents: list, - * recreateApplication: bool, - * closeSessionOnRecreateApplication: bool, - * mailMethod: Yii2Connector::MAIL_CATCH|Yii2Connector::MAIL_IGNORE|Yii2Connector::MAIL_EVENT_AFTER|Yii2Connector::MAIL_EVENT_BEFORE, - * applicationClass: class-string<\yii\base\Application>|null + * configFile: string|null, + * fixturesMethod: string, + * cleanup: bool, + * ignoreCollidingDSN: bool, + * transaction: bool|null, + * entryScript: string, + * entryUrl: string, + * responseCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, + * requestCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, + * mailMethod: Yii2Connector::MAIL_CATCH|Yii2Connector::MAIL_IGNORE|Yii2Connector::MAIL_EVENT_AFTER|Yii2Connector::MAIL_EVENT_BEFORE, + * recreateComponents: list, + * recreateApplication: bool, + * closeSessionOnRecreateApplication: bool, + * applicationClass: class-string<\yii\base\Application>|null * } * * @phpstan-type ValidConfig (ModuleConfig & array{ * transaction: bool|null, - * configFile: string, + * configFile: string * }) - * @phpstan-type SessionBackup array{cookie: array, session: array, headers: array, clientContext: array{ cookieJar: CookieJar, history: History }} - * @phpstan-type ClientConfig array{ - * configFile: string, - * responseCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, - * requestCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, - * recreateComponents: list, - * recreateApplication: bool, - * closeSessionOnRecreateApplication: bool, - * applicationClass: class-string<\yii\base\Application>|null - * } + * @phpstan-type SessionBackup array{ + * cookie: array, + * session: array, + * headers: array, + * clientContext: array{ cookieJar: CookieJar, history: History } + * } */ class Yii2 extends Framework implements ActiveRecord, MultiSession, PartedModule { From 6b706138a29c9916a6dc7e0bdf0e644646eef83f Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 14:15:44 +0100 Subject: [PATCH 07/21] chore(ci): codecept build before phpstan --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7439044..c6bb82a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,8 @@ jobs: with: key: phpstan path: .phpstan-cache + - name: Build test files + run: vendor/bin/codecept build - name: Run PHPStan run: vendor/bin/phpstan - name: Run ECS From 3a5a55551934c32a158df1169400e9c729e99f09 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 14:17:43 +0100 Subject: [PATCH 08/21] chore(ci): check code style before generating files --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6bb82a..21a812d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,12 +19,12 @@ jobs: with: key: phpstan path: .phpstan-cache + - name: Run ECS + run: php vendor/bin/ecs - name: Build test files run: vendor/bin/codecept build - name: Run PHPStan run: vendor/bin/phpstan - - name: Run ECS - run: php vendor/bin/ecs tests: runs-on: ubuntu-latest strategy: From ce1e2fa4399195f74fd79b1683bb130c192f49c6 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 14:27:13 +0100 Subject: [PATCH 09/21] chore(ci): fetch whole history --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 21a812d..dcceebb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,6 +54,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + fetch-depth: 0 persist-credentials: false - uses: actions/setup-node@v4 with: From 7adc7f7bda798139818853569c9b1d3fd3d5d9cf Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 15:08:16 +0100 Subject: [PATCH 10/21] fix: dont add mails that were not sent --- src/Codeception/Lib/Connector/Yii2.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index 864a92d..97c9a57 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -308,7 +308,11 @@ public function startApp(?\yii\log\Logger $logger = null): void match ($this->mailMethod) { self::MAIL_CATCH => $config= $this->mockMailer($config), - self::MAIL_EVENT_AFTER => $config['components']['mailer']['on ' . BaseMailer::EVENT_AFTER_SEND] = fn(MailEvent $event) => $this->emails[] = $event->message, + self::MAIL_EVENT_AFTER => $config['components']['mailer']['on ' . BaseMailer::EVENT_AFTER_SEND] = function(MailEvent $event): void { + if ($event->isSuccessful) { + $this->emails[] = $event->message; + } + }, self::MAIL_EVENT_BEFORE => $config['components']['mailer']['on ' . BaseMailer::EVENT_BEFORE_SEND] = fn(MailEvent $event) => $this->emails[] = $event->message, self::MAIL_IGNORE => null// Do nothing }; From d5eb9a5babac17f58e3ec09bf7213ba6d098e82f Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 15:49:55 +0100 Subject: [PATCH 11/21] chore(cs): fix phpstan types --- src/Codeception/Module/Yii2.php | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 8ed3ab2..a85ec08 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -175,16 +175,7 @@ * * Maintainer: **samdark** * Stability: **stable** - * @phpstan-type ClientConfig array{ - * responseCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, - * requestCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, - * mailMethod: Yii2Connector::MAIL_CATCH|Yii2Connector::MAIL_IGNORE|Yii2Connector::MAIL_EVENT_AFTER|Yii2Connector::MAIL_EVENT_BEFORE, - * recreateComponents: list, - * recreateApplication: bool, - * closeSessionOnRecreateApplication: bool, - * applicationClass: class-string<\yii\base\Application>|null, - * configFile: string - * } + * * @phpstan-type ModuleConfig array{ * configFile: string|null, * fixturesMethod: string, @@ -193,7 +184,6 @@ * transaction: bool|null, * entryScript: string, * entryUrl: string, - * configFile: string, * responseCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, * requestCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, * mailMethod: Yii2Connector::MAIL_CATCH|Yii2Connector::MAIL_IGNORE|Yii2Connector::MAIL_EVENT_AFTER|Yii2Connector::MAIL_EVENT_BEFORE, @@ -202,6 +192,7 @@ * closeSessionOnRecreateApplication: bool, * applicationClass: class-string<\yii\base\Application>|null * } + * * @phpstan-type ClientConfig array{ * configFile: string, * responseCleanMethod: Yii2Connector::CLEAN_CLEAR|Yii2Connector::CLEAN_MANUAL|Yii2Connector::CLEAN_RECREATE, @@ -214,15 +205,9 @@ * } * * @phpstan-type ValidConfig (ModuleConfig & array{ - * transaction: bool|null, - * configFile: string - * }) - * @phpstan-type SessionBackup array{ - * cookie: array, - * session: array, - * headers: array, - * clientContext: array{ cookieJar: CookieJar, history: History } - * } + * transaction: bool|null, +* configFile: string +* }) */ final class Yii2 extends Framework implements ActiveRecord, PartedModule { From d853a393d1368a32cac099e51da7e224d5200d22 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 15:51:44 +0100 Subject: [PATCH 12/21] chore(cs): fix cs --- src/Codeception/Module/Yii2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index a85ec08..4abfb65 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -206,8 +206,8 @@ * * @phpstan-type ValidConfig (ModuleConfig & array{ * transaction: bool|null, -* configFile: string -* }) + * configFile: string + * }) */ final class Yii2 extends Framework implements ActiveRecord, PartedModule { From 602a67cf56e75fe3d95e9cadc351dada90a11d1d Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 15:54:31 +0100 Subject: [PATCH 13/21] chore(sa): regenerate baseline --- phpstan-baseline.neon | 60 ------------------------------------------- 1 file changed, 60 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ce4877d..7e2667d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1092,12 +1092,6 @@ parameters: count: 1 path: tests/_support/FunctionalTester.php - - - message: '#^Method tests\\FunctionalTester\:\:createAndSetCsrfCookie\(\) should return array\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - message: '#^Method tests\\FunctionalTester\:\:dontSee\(\) has parameter \$selector with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -1194,12 +1188,6 @@ parameters: count: 1 path: tests/_support/FunctionalTester.php - - - message: '#^Method tests\\FunctionalTester\:\:getInternalDomains\(\) should return non\-empty\-list\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - message: '#^Method tests\\FunctionalTester\:\:grabAttributeFrom\(\) has parameter \$cssOrXpath with no type specified\.$#' identifier: missingType.parameter @@ -1212,48 +1200,18 @@ parameters: count: 1 path: tests/_support/FunctionalTester.php - - - message: '#^Method tests\\FunctionalTester\:\:grabFixture\(\) should return yii\\db\\ActiveRecord\|yii\\test\\Fixture\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - message: '#^Method tests\\FunctionalTester\:\:grabFixtures\(\) has invalid return type tests\\_generated\\Fixture\.$#' identifier: class.notFound count: 1 path: tests/_support/FunctionalTester.php - - - message: '#^Method tests\\FunctionalTester\:\:grabFixtures\(\) should return array\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - - - message: '#^Method tests\\FunctionalTester\:\:grabLastSentEmail\(\) should return yii\\mail\\BaseMessage\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - message: '#^Method tests\\FunctionalTester\:\:grabMultiple\(\) has parameter \$cssOrXpath with no type specified\.$#' identifier: missingType.parameter count: 1 path: tests/_support/FunctionalTester.php - - - message: '#^Method tests\\FunctionalTester\:\:grabMultiple\(\) should return array\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - - - message: '#^Method tests\\FunctionalTester\:\:grabPageSource\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - message: '#^Method tests\\FunctionalTester\:\:grabRecord\(\) has invalid return type tests\\_generated\\ActiveRecordInterface\.$#' identifier: class.notFound @@ -1266,30 +1224,12 @@ parameters: count: 1 path: tests/_support/FunctionalTester.php - - - message: '#^Method tests\\FunctionalTester\:\:grabRecord\(\) should return array\|yii\\db\\ActiveRecordInterface\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - - - message: '#^Method tests\\FunctionalTester\:\:grabSentEmails\(\) has invalid return type tests\\_generated\\BaseMessage\.$#' - identifier: class.notFound - count: 1 - path: tests/_support/FunctionalTester.php - - message: '#^Method tests\\FunctionalTester\:\:grabSentEmails\(\) has invalid return type tests\\_generated\\MessageInterface\.$#' identifier: class.notFound count: 1 path: tests/_support/FunctionalTester.php - - - message: '#^Method tests\\FunctionalTester\:\:grabSentEmails\(\) should return list\ but returns mixed\.$#' - identifier: return.type - count: 1 - path: tests/_support/FunctionalTester.php - - message: '#^Method tests\\FunctionalTester\:\:grabTextFrom\(\) has parameter \$cssOrXPathOrRegex with no type specified\.$#' identifier: missingType.parameter From 182edcf181a9895e4a3ab4e7f69d16f67f73009a Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 16:01:12 +0100 Subject: [PATCH 14/21] chore(ci): enable automated releases --- .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 dcceebb..3be5a4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: -p "@semantic-release/release-notes-generator" -p conventional-changelog-conventionalcommits -p semantic-release - -- semantic-release --dry-run + -- semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} permissions: From e30e595d19ac059305dd2ff78ceb2df2c0ee621f Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 26 Feb 2025 17:01:41 +0100 Subject: [PATCH 15/21] fix: check using isset instead of !== null for possibly non-existent property --- ecs.php | 3 ++- src/Codeception/Lib/Connector/Yii2.php | 5 +---- tests/Yii.stub | 8 ++++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ecs.php b/ecs.php index 0a1526b..dcbd9ef 100644 --- a/ecs.php +++ b/ecs.php @@ -58,7 +58,8 @@ ForbiddenFunctionsSniff::class => [ 'tests/**', 'console/**' - ] + ], + 'tests/_support/_generated' ]); // $ecsConfig->skip([ diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index 8be068c..80af9d7 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -222,10 +222,7 @@ public function getInternalDomains(): array $domains = [$this->getDomainRegex($urlManager->hostInfo)]; if ($urlManager->enablePrettyUrl) { foreach ($urlManager->rules as $rule) { - /** - * @var \yii\web\UrlRule $rule - */ - if ($rule->host !== null) { + if (isset($rule->host)) { $domains[] = $this->getDomainRegex($rule->host); } } diff --git a/tests/Yii.stub b/tests/Yii.stub index bbfd44a..87cd9aa 100644 --- a/tests/Yii.stub +++ b/tests/Yii.stub @@ -95,4 +95,12 @@ namespace yii\web { public function setScriptFile($path): void {} } + interface UrlRuleInterface{} + class UrlManager { + /** + * @var UrlRuleInterface[] $rules; + */ + public array $rules = []; + } + } From d72f1446146bed998de2c2df9e6f2da3228883c8 Mon Sep 17 00:00:00 2001 From: TavoNiievez Date: Fri, 28 Feb 2025 12:48:19 -0500 Subject: [PATCH 16/21] fix: improve exception handling and code consistency - Standardized `RuntimeException` usage by removing redundant namespace references. - Refactored `findAndLoginUser` for clarity using ternary assignment. - Used `readonly` property for `ignoreCollidingDSN` in `TransactionForcer`. - Optimized server params handling by introducing `getServerParams()`. - Improved configuration validation messages for clean and mail methods. --- src/Codeception/Lib/Connector/Yii2.php | 26 ++++------ src/Codeception/Lib/Connector/Yii2/Logger.php | 9 ++-- .../Lib/Connector/Yii2/TransactionForcer.php | 17 +++---- src/Codeception/Module/Yii2.php | 49 ++++++++----------- 4 files changed, 43 insertions(+), 58 deletions(-) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index 8be068c..8662836 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -10,6 +10,7 @@ use Codeception\Lib\Connector\Yii2\TestMailer; use Codeception\Util\Debug; use InvalidArgumentException; +use RuntimeException; use Symfony\Component\BrowserKit\AbstractBrowser as Client; use Symfony\Component\BrowserKit\Cookie; use Symfony\Component\BrowserKit\CookieJar; @@ -142,14 +143,14 @@ protected function getApplication(): \yii\base\Application if (! isset(Yii::$app)) { $this->startApp(); } - return Yii::$app ?? throw new \RuntimeException('Failed to create Yii2 application'); + return Yii::$app ?? throw new RuntimeException('Failed to create Yii2 application'); } private function getWebRequest(): YiiRequest { $request = $this->getApplication()->request; if (! $request instanceof YiiRequest) { - throw new \RuntimeException('Request component is not of type ' . YiiRequest::class); + throw new RuntimeException('Request component is not of type ' . YiiRequest::class); } return $request; } @@ -172,8 +173,7 @@ public function resetApplication(bool $closeSession = true): void * Finds and logs in a user * * @internal - * @throws ConfigurationException - * @throws \RuntimeException + * @throws ConfigurationException|RuntimeException */ public function findAndLoginUser(int|string|IdentityInterface $user): void { @@ -183,15 +183,9 @@ public function findAndLoginUser(int|string|IdentityInterface $user): void throw new ConfigurationException('The user component is not configured'); } - if ($user instanceof IdentityInterface) { - $identity = $user; - } else { - // class name implementing IdentityInterface - $identityClass = $userComponent->identityClass; - $identity = $identityClass::findIdentity($user); - if ($identity === null) { - throw new \RuntimeException('User not found'); - } + $identity = $user instanceof IdentityInterface ? $user : ($userComponent->identityClass)::findIdentity($user); + if ($identity === null) { + throw new RuntimeException('User not found'); } $userComponent->login($identity); } @@ -273,7 +267,7 @@ function ($matches) use (&$parameters): string { ); } if ($template === null) { - throw new \RuntimeException("Failed to parse domain regex"); + throw new RuntimeException("Failed to parse domain regex"); } $template = preg_quote($template); $template = strtr($template, $parameters); @@ -411,9 +405,9 @@ public function doRequest(object $request): Response $content = ob_get_clean(); if (empty($content) && ! empty($yiiResponse->content) && ! isset($yiiResponse->stream)) { - throw new \RuntimeException('No content was sent from Yii application'); + throw new RuntimeException('No content was sent from Yii application'); } elseif ($content === false) { - throw new \RuntimeException('Failed to get output buffer'); + throw new RuntimeException('Failed to get output buffer'); } return new Response($content, $yiiResponse->statusCode, $yiiResponse->getHeaders()->toArray()); diff --git a/src/Codeception/Lib/Connector/Yii2/Logger.php b/src/Codeception/Lib/Connector/Yii2/Logger.php index 69ebd4e..2443c39 100644 --- a/src/Codeception/Lib/Connector/Yii2/Logger.php +++ b/src/Codeception/Lib/Connector/Yii2/Logger.php @@ -5,6 +5,7 @@ namespace Codeception\Lib\Connector\Yii2; use Codeception\Util\Debug; +use SplQueue; use yii\base\Exception as YiiException; use yii\helpers\VarDumper; use yii\log\Logger as YiiLogger; @@ -12,9 +13,9 @@ final class Logger extends YiiLogger { /** - * @var \SplQueue + * @var SplQueue */ - private \SplQueue $logQueue; + private SplQueue $logQueue; /** * @param array $config @@ -24,7 +25,7 @@ public function __construct( array $config = [] ) { parent::__construct($config); - $this->logQueue = new \SplQueue(); + $this->logQueue = new SplQueue(); } public function init(): void @@ -69,7 +70,7 @@ public function log($message, $level, $category = 'application'): void public function getAndClearLog(): string { $logs = iterator_to_array($this->logQueue); - $this->logQueue = new \SplQueue(); + $this->logQueue = new SplQueue(); return implode(PHP_EOL, $logs) . PHP_EOL; } } diff --git a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php index e93ac44..1322f86 100644 --- a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php +++ b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php @@ -29,7 +29,7 @@ final class TransactionForcer extends ConnectionWatcher private array $transactions = []; public function __construct( - private bool $ignoreCollidingDSN + private readonly bool $ignoreCollidingDSN ) { parent::__construct(); } @@ -45,12 +45,12 @@ protected function connectionOpened(Connection $connection): void $key = md5( json_encode( [ - 'dsn' => $connection->dsn, - 'user' => $connection->username, - 'pass' => $connection->password, - 'attributes' => $connection->attributes, - 'emulatePrepare' => $connection->emulatePrepare, - 'charset' => $connection->charset, + 'dsn' => $connection->dsn, + 'user' => $connection->username, + 'pass' => $connection->password, + 'attributes' => $connection->attributes, + 'emulatePrepare' => $connection->emulatePrepare, + 'charset' => $connection->charset, ], JSON_THROW_ON_ERROR ) @@ -87,9 +87,6 @@ protected function connectionOpened(Connection $connection): void public function rollbackAll(): void { - /** - * @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 4abfb65..54c289d 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -15,6 +15,7 @@ use Codeception\Lib\Interfaces\ActiveRecord; use Codeception\Lib\Interfaces\PartedModule; use Codeception\TestInterface; +use Exception; use PHPUnit\Framework\Assert; use ReflectionClass; use RuntimeException; @@ -276,7 +277,9 @@ public function _initialize(): void $this->defineConstants(); $this->server = $_SERVER; - $this->initServerGlobal(); + // Adds the required server params. Note this is done separately from the request cycle since someone might call + // `Url::to` before doing a request, which would instantiate the request component with incorrect server params. + $_SERVER = [...$_SERVER, $this->getServerParams()]; } /** @@ -294,26 +297,27 @@ protected function onReconfigure(): void } /** - * Adds the required server params. - * Note this is done separately from the request cycle since someone might call - * `Url::to` before doing a request, which would instantiate the request component with incorrect server params. + * @return array{ + * SCRIPT_FILENAME: string, + * SCRIPT_NAME: string, + * SERVER_NAME: string, + * SERVER_PORT: string|int, + * HTTPS: bool + * } */ - private function initServerGlobal(): void + private function getServerParams(): array { $entryUrl = $this->config['entryUrl']; $parsedUrl = parse_url($entryUrl); $entryFile = $this->config['entryScript'] ?: basename($entryUrl); $entryScript = $this->config['entryScript'] ?: ($parsedUrl['path'] ?? ''); - $_SERVER = array_merge( - $_SERVER, - [ + return [ 'SCRIPT_FILENAME' => $entryFile, 'SCRIPT_NAME' => $entryScript, 'SERVER_NAME' => $parsedUrl['host'] ?? '', 'SERVER_PORT' => $parsedUrl['port'] ?? '80', 'HTTPS' => isset($parsedUrl['scheme']) && $parsedUrl['scheme'] === 'https', - ] - ); + ]; } /** @@ -335,23 +339,24 @@ protected function validateConfig(): void "The application config file does not exist: " . $pathToConfig, ); } - $validMethods = implode(", ", Yii2Connector::CLEAN_METHODS); + $validCleanMethods = 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: " . $validCleanMethods, ); } + $validMailMethods = implode(", ", Yii2Connector::MAIL_METHODS); if (! in_array($this->config['mailMethod'], Yii2Connector::MAIL_METHODS, true)) { throw new ModuleConfigException( self::class, - "The mail method must be one of: " . $validMethods + "The mail method must be one of: " . $validMailMethods ); } 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: " . $validCleanMethods, ); } } @@ -377,19 +382,7 @@ private function configureClient(array $settings): void */ protected function recreateClient(): void { - $entryUrl = $this->config['entryUrl']; - $parsedUrl = parse_url($entryUrl); - $entryFile = $this->config['entryScript'] ?: basename($entryUrl); - $entryScript = $this->config['entryScript'] ?: ($parsedUrl['path'] ?? ''); - $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->client = new Yii2Connector($this->getServerParams()); $this->validateConfig(); $this->configureClient($this->config); } @@ -463,7 +456,7 @@ public function _after(TestInterface $test): void } /** - * @param \Exception $fail + * @param Exception $fail */ public function _failed(TestInterface $test, $fail): void { From 8c789c94c1eb8c4ae0b5873c3a17ea9fda31994d Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 1 Apr 2025 14:50:04 +0200 Subject: [PATCH 17/21] fix: server params refactor broke format --- src/Codeception/Module/Yii2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 54c289d..30f3372 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -279,7 +279,7 @@ public function _initialize(): void $this->server = $_SERVER; // Adds the required server params. Note this is done separately from the request cycle since someone might call // `Url::to` before doing a request, which would instantiate the request component with incorrect server params. - $_SERVER = [...$_SERVER, $this->getServerParams()]; + $_SERVER = [...$_SERVER, ...$this->getServerParams()]; } /** From 7779f7e208471eae076ee8462906a388f0b71d7c Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Tue, 1 Apr 2025 15:05:31 +0200 Subject: [PATCH 18/21] chore: fix phsptan config to allow bare array typehint --- phpstan.neon | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index fb2d525..e695b2d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -17,6 +17,8 @@ parameters: checkMaybeUndefinedVariables: true treatPhpDocTypesAsCertain: false ignoreErrors: + - identifier: missingType.iterableValue + path: tests/_support/_generated/FunctionalTesterActions.php - identifier: return.type path: tests/_support/_generated/FunctionalTesterActions.php message: "# but returns mixed.$#" From b480bf18befdc8ede138f2f3a6b09a8989d11cb9 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Thu, 29 May 2025 15:28:38 +0200 Subject: [PATCH 19/21] fix: fixes #131 yiilogger may not be initialized in _failed --- src/Codeception/Module/Yii2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 30f3372..96cfa41 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -460,8 +460,8 @@ public function _after(TestInterface $test): void */ public function _failed(TestInterface $test, $fail): void { - $log = $this->yiiLogger->getAndClearLog(); - if ($log !== '') { + $log = $this->yiiLogger?->getAndClearLog(); + if (isset($log) && $log !== '') { $test->getMetadata()->addReport('yii-log', $log); } From 9e0ea245e852572344cde1fccde093052194a683 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Fri, 30 May 2025 10:06:24 +0200 Subject: [PATCH 20/21] chore: make logger explicitly nullable --- src/Codeception/Module/Yii2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index 96cfa41..3a19cf8 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -256,7 +256,7 @@ final class Yii2 extends Framework implements ActiveRecord, PartedModule */ private array $server; - private Logger $yiiLogger; + private null|Logger $yiiLogger = null; private function getClient(): Yii2Connector { @@ -292,7 +292,7 @@ protected function onReconfigure(): void $this->getClient()->resetApplication(); $this->validateConfig(); $this->configureClient($this->config); - $this->yiiLogger->getAndClearLog(); + $this->yiiLogger?->getAndClearLog(); $this->getClient()->startApp($this->yiiLogger); } From f3ce1cae1ec4d3ddd0c2f235191e3ec378406418 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Fri, 30 May 2025 10:22:05 +0200 Subject: [PATCH 21/21] chore: fixed SA errors due to phpstan update --- phpstan-baseline.neon | 6 ------ src/Codeception/Lib/Connector/Yii2.php | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7e2667d..01178ed 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1698,12 +1698,6 @@ parameters: count: 1 path: tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php - - - message: '#^Method app\\pageCacheHeaderAlreadySent\\controllers\\UserController\:\:behaviors\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/cases/pageCacheHeaderAlreadySent/controllers/UserController.php - - message: '#^Method PageCest\:\:testCache\(\) has no return type specified\.$#' identifier: missingType.return diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index 206c924..379bfc1 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -305,6 +305,7 @@ public function startApp(?\yii\log\Logger $logger = null): void self::MAIL_IGNORE => null// Do nothing }; + // @phpstan-ignore argument.templateType $app = Yii::createObject($config); if (! $app instanceof \yii\base\Application) { throw new ModuleConfigException($this, "Failed to initialize Yii2 app");