diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 02e24fb..445214c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,6 +26,8 @@ jobs: - name: Install dependencies run: composer install --prefer-dist --no-progress --no-interaction --no-suggest + - name: Run PHPStan + run: php vendor/bin/phpstan - name: Run test suite run: | diff --git a/composer.json b/composer.json index ee7c669..a447850 100644 --- a/composer.json +++ b/composer.json @@ -27,8 +27,9 @@ "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" + "codeception/module-asserts": ">= 3.0", + "codeception/module-filesystem": "> 3.0", + "phpstan/phpstan": "^1.10" }, "autoload":{ "classmap": ["src/"] diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..efda0f0 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,20 @@ +#includes: +# - phpstan-baseline.neon +parameters: + reportUnmatchedIgnoredErrors: true + dynamicConstantNames: + - CONSOLE + - YII_DEBUG + level: 5 + paths: + - src + checkMaybeUndefinedVariables: true + checkGenericClassInNonGenericObjectType: 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: '~^Variable \$_COOKIE in isset\(\) always exists and is not nullable.$~' + path: 'src/' + stubFiles: + - tests/Yii.stub diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index d18a3a3..b7c7519 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -7,6 +7,8 @@ use Codeception\Util\Debug; use Symfony\Component\BrowserKit\AbstractBrowser as Client; use Symfony\Component\BrowserKit\Cookie; +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\BrowserKit\History; use Symfony\Component\BrowserKit\Response; use Yii; use yii\base\ExitException; @@ -15,8 +17,10 @@ use yii\mail\MessageInterface; use yii\web\Application; use yii\web\ErrorHandler; +use yii\web\IdentityInterface; use yii\web\Request; use yii\web\Response as YiiResponse; +use yii\web\User; class Yii2 extends Client { @@ -81,23 +85,22 @@ class Yii2 extends Client /** * @var bool whether to close the session in between requests inside a single test, if recreateApplication is set to true */ - public $closeSessionOnRecreateApplication = true; + public bool $closeSessionOnRecreateApplication = true; /** - * @var string The FQN of the application class to use. In a default Yii setup, should be either `yii\web\Application` + * @var class-string The FQN of the application class to use. In a default Yii setup, should be either `yii\web\Application` * or `yii\console\Application` */ - public $applicationClass = null; + public string|null $applicationClass = null; - private $emails = []; + private array $emails = []; /** - * @return \yii\web\Application - * * @deprecated since 2.5, will become protected in 3.0. Directly access to \Yii::$app if you need to interact with it. + * @internal */ - public function getApplication() + public function getApplication(): \yii\base\Application { if (!isset(Yii::$app)) { $this->startApp(); @@ -105,10 +108,7 @@ public function getApplication() return Yii::$app; } - /** - * @param bool $closeSession - */ - public function resetApplication($closeSession = true) + public function resetApplication(bool $closeSession = true): void { codecept_debug('Destroying application'); if (true === $closeSession) { @@ -127,14 +127,14 @@ public function resetApplication($closeSession = true) /** * Finds and logs in a user * @internal - * @param $user * @throws ConfigurationException * @throws \RuntimeException */ - public function findAndLoginUser($user) + public function findAndLoginUser(int|string|IdentityInterface $user): void { $app = $this->getApplication(); - if (!$app->has('user')) { + $user = $app->get('user'); + if (!$user instanceof User) { throw new ConfigurationException('The user component is not configured'); } @@ -142,25 +142,13 @@ public function findAndLoginUser($user) $identity = $user; } else { // class name implementing IdentityInterface - $identityClass = $app->user->identityClass; + $identityClass = $user->identityClass; $identity = call_user_func([$identityClass, 'findIdentity'], $user); if (!isset($identity)) { throw new \RuntimeException('User not found'); } } - $app->user->login($identity); - } - - /** - * Masks a value - * @internal - * @param string $val - * @return string - * @see \yii\base\Security::maskToken - */ - public function maskToken($val) - { - return $this->getApplication()->security->maskToken($val); + $user->login($identity); } /** @@ -169,7 +157,7 @@ public function maskToken($val) * @param string $value The value of the cookie * @return string The value to send to the browser */ - public function hashCookieData($name, $value) + public function hashCookieData($name, $value): string { $app = $this->getApplication(); if (!$app->request->enableCookieValidation) { @@ -182,7 +170,7 @@ public function hashCookieData($name, $value) * @internal * @return array List of regex patterns for recognized domain names */ - public function getInternalDomains() + public function getInternalDomains(): array { /** @var \yii\web\UrlManager $urlManager */ $urlManager = $this->getApplication()->urlManager; @@ -202,7 +190,7 @@ public function getInternalDomains() * @internal * @return array List of sent emails */ - public function getEmails() + public function getEmails(): array { return $this->emails; } @@ -211,7 +199,7 @@ public function getEmails() * Deletes all stored emails. * @internal */ - public function clearEmails() + public function clearEmails(): void { $this->emails = []; } @@ -230,11 +218,8 @@ public function getComponent($name) /** * Getting domain regex from rule host template - * - * @param string $template - * @return string */ - private function getDomainRegex($template) + private function getDomainRegex(string $template): string { if (preg_match('#https?://(.*)#', $template, $matches)) { $template = $matches[1]; @@ -259,24 +244,13 @@ function ($matches) use (&$parameters) { /** * Gets the name of the CSRF param. * @internal - * @return string */ - public function getCsrfParamName() + public function getCsrfParamName(): string { return $this->getApplication()->request->csrfParam; } - /** - * @internal - * @param $params - * @return mixed - */ - public function createUrl($params) - { - return is_array($params) ?$this->getApplication()->getUrlManager()->createUrl($params) : $params; - } - - public function startApp(\yii\log\Logger $logger = null) + public function startApp(\yii\log\Logger $logger = null): void { codecept_debug('Starting application'); $config = require($this->configFile); @@ -306,12 +280,9 @@ public function startApp(\yii\log\Logger $logger = null) } /** - * * @param \Symfony\Component\BrowserKit\Request $request - * - * @return \Symfony\Component\BrowserKit\Response */ - public function doRequest(object $request) + public function doRequest(object $request): \Symfony\Component\BrowserKit\Response { $_COOKIE = $request->getCookies(); $_SERVER = $request->getServer(); @@ -343,6 +314,9 @@ public function doRequest(object $request) $this->beforeRequest(); $app = $this->getApplication(); + if (!$app instanceof Application) { + throw new ConfigurationException("Application is not a web application"); + } // disabling logging. Logs are slowing test execution down foreach ($app->log->targets as $target) { @@ -407,14 +381,13 @@ protected function revertErrorHandler() /** * Encodes the cookies and adds them to the headers. - * @param \yii\web\Response $response * @throws \yii\base\InvalidConfigException */ protected function encodeCookies( YiiResponse $response, Request $request, Security $security - ) { + ): void { if ($request->enableCookieValidation) { $validationKey = $request->cookieValidationKey; } @@ -422,7 +395,8 @@ protected function encodeCookies( foreach ($response->getCookies() as $cookie) { /** @var \yii\web\Cookie $cookie */ $value = $cookie->value; - if ($cookie->expire != 1 && isset($validationKey)) { + // Expire = 1 means we're removing the cookie + if ($cookie->expire !== 1 && isset($validationKey)) { $data = version_compare(Yii::getVersion(), '2.0.2', '>') ? [$cookie->name, $cookie->value] : $cookie->value; @@ -443,10 +417,10 @@ protected function encodeCookies( /** * Replace mailer with in memory mailer - * @param array $config Original configuration - * @return array New configuration + * @param array $config Original configuration + * @return array New configuration */ - protected function mockMailer(array $config) + protected function mockMailer(array $config): array { // options that make sense for mailer mock $allowedOptions = [ @@ -489,9 +463,10 @@ public function restart(): void /** * Return an assoc array with the client context: cookieJar, history. * - * @return array + * @internal + * @return array{ cookieJar: CookieJar, history: History } */ - public function getContext() + public function getContext(): array { return [ 'cookieJar' => $this->cookieJar, @@ -499,20 +474,12 @@ public function getContext() ]; } - /** - * Reset the client context: empty cookieJar and history. - */ - public function removeContext() - { - parent::restart(); - } - /** * Set the context, see getContext(). * - * @param array $context + * @param array{ cookieJar: CookieJar, history: History } $context */ - public function setContext(array $context) + public function setContext(array $context): void { $this->cookieJar = $context['cookieJar']; $this->history = $context['history']; @@ -522,10 +489,11 @@ public function setContext(array $context) * This functions closes the session of the application, if the application exists and has a session. * @internal */ - public function closeSession() + public function closeSession(): void { - if (isset(\Yii::$app) && \Yii::$app->has('session', true)) { - \Yii::$app->session->close(); + $app = \Yii::$app; + if ($app instanceof \yii\web\Application && $app->has('session', true)) { + $app->session->close(); } } @@ -533,7 +501,7 @@ public function closeSession() * Resets the applications' response object. * The method used depends on the module configuration. */ - protected function resetResponse(Application $app) + protected function resetResponse(Application $app): void { $method = $this->responseCleanMethod; // First check the current response object. @@ -566,7 +534,7 @@ protected function resetResponse(Application $app) } } - protected function resetRequest(Application $app) + protected function resetRequest(Application $app): void { $method = $this->requestCleanMethod; $request = $app->request; @@ -596,8 +564,8 @@ protected function resetRequest(Application $app) $request->setScriptFile(null); $request->setScriptUrl(null); $request->setUrl(null); - $request->setPort(null); - $request->setSecurePort(null); + $request->setPort(0); + $request->setSecurePort(0); $request->setAcceptableContentTypes(null); $request->setAcceptableLanguages(null); @@ -610,7 +578,7 @@ protected function resetRequest(Application $app) /** * Called before each request, preparation happens here. */ - protected function beforeRequest() + protected function beforeRequest(): void { if ($this->recreateApplication) { $this->resetApplication($this->closeSessionOnRecreateApplication); @@ -619,6 +587,9 @@ protected function beforeRequest() $application = $this->getApplication(); + if (!$application instanceof Application) { + throw new ConfigurationException('Application must be an instance of web application when doing requests'); + } $this->resetResponse($application); $this->resetRequest($application); diff --git a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php index eae5382..291c921 100644 --- a/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php +++ b/src/Codeception/Lib/Connector/Yii2/ConnectionWatcher.php @@ -13,10 +13,10 @@ */ class ConnectionWatcher { - private $handler; + private \Closure $handler; /** @var Connection[] */ - private $connections = []; + private array $connections = []; public function __construct() { @@ -27,27 +27,25 @@ public function __construct() }; } - protected function connectionOpened(Connection $connection) + protected function connectionOpened(Connection $connection): void { $this->debug('Connection opened!'); - if ($connection instanceof Connection) { - $this->connections[] = $connection; - } + $this->connections[] = $connection; } - public function start() + public function start(): void { Event::on(Connection::class, Connection::EVENT_AFTER_OPEN, $this->handler); $this->debug('watching new connections'); } - public function stop() + public function stop(): void { Event::off(Connection::class, Connection::EVENT_AFTER_OPEN, $this->handler); $this->debug('no longer watching new connections'); } - public function closeAll() + public function closeAll(): void { $count = count($this->connections); $this->debug("closing all ($count) connections"); @@ -56,7 +54,7 @@ public function closeAll() } } - protected function debug($message) + protected function debug($message): void { $title = (new \ReflectionClass($this))->getShortName(); if (is_array($message) or is_object($message)) { diff --git a/src/Codeception/Lib/Connector/Yii2/Logger.php b/src/Codeception/Lib/Connector/Yii2/Logger.php index 3135962..b229f32 100644 --- a/src/Codeception/Lib/Connector/Yii2/Logger.php +++ b/src/Codeception/Lib/Connector/Yii2/Logger.php @@ -2,32 +2,30 @@ namespace Codeception\Lib\Connector\Yii2; use Codeception\Util\Debug; +use yii\helpers\VarDumper; class Logger extends \yii\log\Logger { - /** - * @var \SplQueue - */ - private $logQueue; - - /** - * @var int - */ - private $maxLogItems; + private \SplQueue $logQueue; - public function __construct($maxLogItems = 5, $config = []) + public function __construct(private int $maxLogItems = 5, $config = []) { parent::__construct($config); $this->logQueue = new \SplQueue(); - $this->maxLogItems = $maxLogItems; } - public function init() + public function init(): void { // overridden to prevent register_shutdown_function } - public function log($message, $level, $category = 'application') + /** + * @param string|array|\yii\base\Exception $message + * @param $level + * @param $category + * @return void + */ + public function log($message, $level, $category = 'application'): void { if (!in_array($level, [ \yii\log\Logger::LEVEL_INFO, @@ -36,7 +34,7 @@ public function log($message, $level, $category = 'application') ])) { return; } - if (strpos($category, 'yii\db\Command')===0) { + if (str_starts_with($category, 'yii\db\Command')) { return; // don't log queries } @@ -45,7 +43,7 @@ public function log($message, $level, $category = 'application') $message = $message->__toString(); } - $logMessage = "[$category] " . \yii\helpers\VarDumper::export($message); + $logMessage = "[$category] " . VarDumper::export($message); Debug::debug($logMessage); @@ -55,10 +53,7 @@ public function log($message, $level, $category = 'application') } } - /** - * @return string - */ - public function getAndClearLog() + public function getAndClearLog(): string { $completeStr = ''; foreach ($this->logQueue as $item) { diff --git a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php index 060b8a1..fbc08f2 100644 --- a/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php +++ b/src/Codeception/Lib/Connector/Yii2/TransactionForcer.php @@ -3,6 +3,7 @@ namespace Codeception\Lib\Connector\Yii2; +use Codeception\Util\Debug; use yii\base\Event; use yii\db\Connection; use yii\db\Transaction; @@ -30,7 +31,7 @@ public function __construct( } - protected function connectionOpened(Connection $connection) + protected function connectionOpened(Connection $connection): void { parent::connectionOpened($connection); /** @@ -80,7 +81,7 @@ protected function connectionOpened(Connection $connection) $this->transactions[$key] = $connection->beginTransaction(); } - public function rollbackAll() + public function rollbackAll(): void { /** @var Transaction $transaction */ foreach ($this->transactions as $transaction) { diff --git a/src/Codeception/Module/Yii2.php b/src/Codeception/Module/Yii2.php index fb1c408..06d09d8 100644 --- a/src/Codeception/Module/Yii2.php +++ b/src/Codeception/Module/Yii2.php @@ -1,25 +1,25 @@ client) && !$this->client instanceof \Codeception\Lib\Connector\Yii2) { + throw new \RuntimeException('The Yii2 module must be used with the Yii2 browser client'); + } + return $this->client; + } + + public function _initialize(): void { if ($this->config['transaction'] === null) { $this->config['transaction'] = $this->backupConfig['transaction'] = $this->config['cleanup']; @@ -234,13 +236,13 @@ public function _initialize() * Module configuration changed inside a test. * We always re-create the application. */ - protected function onReconfigure() + protected function onReconfigure(): void { parent::onReconfigure(); - $this->client->resetApplication(); + $this->getClient()->resetApplication(); $this->configureClient($this->config); $this->yiiLogger->getAndClearLog(); - $this->client->startApp($this->yiiLogger); + $this->getClient()->startApp($this->yiiLogger); } /** @@ -248,7 +250,7 @@ protected function onReconfigure() * 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. */ - private function initServerGlobal() + private function initServerGlobal(): void { $entryUrl = $this->config['entryUrl']; @@ -290,22 +292,22 @@ protected function validateConfig(): void } } - protected function configureClient(array $settings) + protected function configureClient(array $settings): void { $settings['configFile'] = codecept_absolute_path($settings['configFile']); foreach ($settings as $key => $value) { if (property_exists($this->client, $key)) { - $this->client->$key = $value; + $this->getClient()->$key = $value; } } - $this->client->resetApplication(); + $this->getClient()->resetApplication(); } /** * Instantiates the client based on module configuration */ - protected function recreateClient() + protected function recreateClient(): void { $entryUrl = $this->config['entryUrl']; $entryFile = $this->config['entryScript'] ?: basename($entryUrl); @@ -322,13 +324,13 @@ protected function recreateClient() $this->configureClient($this->config); } - public function _before(TestInterface $test) + public function _before(TestInterface $test): void { $this->recreateClient(); $this->yiiLogger = new Yii2Connector\Logger(); - $this->client->startApp($this->yiiLogger); + $this->getClient()->startApp($this->yiiLogger); - $this->connectionWatcher = new Yii2Connector\ConnectionWatcher(); + $this->connectionWatcher = new ConnectionWatcher(); $this->connectionWatcher->start(); // load fixtures before db transaction @@ -346,16 +348,14 @@ public function _before(TestInterface $test) /** * load fixtures before db transaction - * - * @param mixed $test instance of test class */ - private function loadFixtures($test) + private function loadFixtures(object $test): void { $this->debugSection('Fixtures', 'Loading fixtures'); if (empty($this->loadedFixtures) && method_exists($test, $this->_getConfig('fixturesMethod')) ) { - $connectionWatcher = new Yii2Connector\ConnectionWatcher(); + $connectionWatcher = new ConnectionWatcher(); $connectionWatcher->start(); $this->haveFixtures(call_user_func([$test, $this->_getConfig('fixturesMethod')])); $connectionWatcher->stop(); @@ -364,7 +364,7 @@ private function loadFixtures($test) $this->debugSection('Fixtures', 'Done'); } - public function _after(TestInterface $test) + public function _after(TestInterface $test): void { $_SESSION = []; $_FILES = []; @@ -382,9 +382,7 @@ public function _after(TestInterface $test) $this->loadedFixtures = []; } - if ($this->client !== null) { - $this->client->resetApplication(); - } + $this->getClient()?->resetApplication(); if (isset($this->connectionWatcher)) { $this->connectionWatcher->stop(); @@ -395,24 +393,25 @@ public function _after(TestInterface $test) parent::_after($test); } - public function _failed(TestInterface $test, $fail) + public function _failed(TestInterface $test, $fail): void { - if ($this->yiiLogger && $log = $this->yiiLogger->getAndClearLog()) { + $log = $this->yiiLogger->getAndClearLog(); + if ($log !== '') { $test->getMetadata()->addReport('yii-log', $log); } parent::_failed($test, $fail); } - protected function startTransactions() + protected function startTransactions(): void { if ($this->config['transaction']) { - $this->transactionForcer = new Yii2Connector\TransactionForcer($this->config['ignoreCollidingDSN']); + $this->transactionForcer = new TransactionForcer($this->config['ignoreCollidingDSN']); $this->transactionForcer->start(); } } - protected function rollbackTransactions() + protected function rollbackTransactions(): void { if (isset($this->transactionForcer)) { $this->transactionForcer->rollbackAll(); @@ -441,13 +440,12 @@ public function _parts(): array * ``` * Requires the `user` component to be enabled and configured. * - * @param $user * @throws \Codeception\Exception\ModuleException */ - public function amLoggedInAs($user) + public function amLoggedInAs(int|string|IdentityInterface $user): void { try { - $this->client->findAndLoginUser($user); + $this->getClient()?->findAndLoginUser($user); } catch (ConfigurationException $e) { throw new ModuleException($this, $e->getMessage()); } catch (\RuntimeException $e) { @@ -491,7 +489,7 @@ public function amLoggedInAs($user) * @param $fixtures * @part fixtures */ - public function haveFixtures($fixtures) + public function haveFixtures($fixtures): void { if (empty($fixtures)) { return; @@ -571,15 +569,15 @@ public function grabFixture($name, $index = null) * $user_id = $I->haveRecord('app\models\User', array('name' => 'Davert')); * ?> * ``` - * - * @param $model - * @param array $attributes + * @template T of \yii\db\ActiveRecord + * @param class-string $model + * @param array $attributes * @return mixed * @part orm */ - public function haveRecord($model, $attributes = []) + public function haveRecord(string $model, $attributes = []): mixed { - /** @var $record \yii\db\ActiveRecord * */ + /** @var T $record **/ $record = \Yii::createObject($model); $record->setAttributes($attributes, false); $res = $record->save(false); @@ -596,8 +594,8 @@ public function haveRecord($model, $attributes = []) * $I->seeRecord('app\models\User', array('name' => 'davert')); * ``` * - * @param $model - * @param array $attributes + * @param class-string<\yii\db\ActiveRecord> $model + * @param array $attributes * @part orm */ public function seeRecord(string $model, array $attributes = []): void @@ -616,8 +614,8 @@ public function seeRecord(string $model, array $attributes = []): void * $I->dontSeeRecord('app\models\User', array('name' => 'davert')); * ``` * - * @param $model - * @param array $attributes + * @param class-string<\yii\db\ActiveRecord> $model + * @param array $attributes * @part orm */ public function dontSeeRecord(string $model, array $attributes = []): void @@ -636,35 +634,34 @@ public function dontSeeRecord(string $model, array $attributes = []): void * $category = $I->grabRecord('app\models\User', array('name' => 'davert')); * ``` * - * @param $model - * @param array $attributes - * @return mixed + * @param class-string<\yii\db\ActiveRecord> $model + * @param array $attributes * @part orm */ - public function grabRecord(string $model, array $attributes = []): mixed + public function grabRecord(string $model, array $attributes = []): \yii\db\ActiveRecord|null|array { return $this->findRecord($model, $attributes); } /** - * @param string $model Class name - * @param array $attributes - * @return mixed + * @param class-string<\yii\db\ActiveRecord> $model Class name + * @param array $attributes */ - protected function findRecord(string $model, array $attributes = []): mixed + protected function findRecord(string $model, array $attributes = []): \yii\db\ActiveRecord | null | array { if (!class_exists($model)) { throw new \RuntimeException("Class $model does not exist"); } $rc = new \ReflectionClass($model); if ($rc->hasMethod('find') + /** @phpstan-ignore-next-line */ && ($findMethod = $rc->getMethod('find')) && $findMethod->isStatic() && $findMethod->isPublic() && $findMethod->getNumberOfRequiredParameters() === 0 ) { $activeQuery = $findMethod->invoke(null); - if ($activeQuery instanceof QueryInterface) { + if ($activeQuery instanceof ActiveQueryInterface) { return $activeQuery->andWhere($attributes)->one(); } @@ -693,41 +690,6 @@ public function amOnRoute(string $route, array $params = []): void $this->amOnPage(Url::to($params)); } - /** - * Opens the page for the given relative URI or route. - * - * ``` php - * amOnPage('/'); - * // opens /register page - * $I->amOnPage('/register'); - * ``` - * - * @param string $page the page URI - */ - public function amOnPage(string $page): void - { - parent::amOnPage($page); - } - - /** - * To support to use the behavior of urlManager component - * for the methods like this: amOnPage(), sendAjaxRequest() and etc. - * @param $method - * @param $uri - * @param array $parameters - * @param array $files - * @param array $server - * @param null $content - * @param bool $changeHistory - * @return mixed - */ - protected function clientRequest(string $method, string $uri, array $parameters = [], array $files = [], array $server = [], string $content = null, bool $changeHistory = true): SymfonyCrawler - { - return parent::clientRequest($method, $this->client->createUrl($uri), $parameters, $files, $server, $content, $changeHistory); - } - /** * Gets a component from the Yii container. Throws an exception if the * component is not available @@ -737,15 +699,13 @@ protected function clientRequest(string $method, string $uri, array $parameters * $mailer = $I->grabComponent('mailer'); * ``` * - * @param $component - * @return mixed * @throws \Codeception\Exception\ModuleException * @deprecated in your tests you can use \Yii::$app directly. */ - public function grabComponent(mixed $component) + public function grabComponent(string $component): null|object { try { - return $this->client->getComponent($component); + return $this->getClient()->getComponent($component); } catch (ConfigurationException $e) { throw new ModuleException($this, $e->getMessage()); } @@ -805,7 +765,7 @@ public function dontSeeEmailIsSent(): void public function grabSentEmails(): array { try { - return $this->client->getEmails(); + return $this->getClient()->getEmails(); } catch (ConfigurationException $e) { throw new ModuleException($this, $e->getMessage()); } @@ -838,7 +798,7 @@ public function grabLastSentEmail(): object */ public function getInternalDomains(): array { - return $this->client->getInternalDomains(); + return $this->getClient()->getInternalDomains(); } private function defineConstants(): void @@ -856,7 +816,7 @@ private function defineConstants(): void */ public function setCookie($name, $val, $params = []) { - parent::setCookie($name, $this->client->hashCookieData($name, $val), $params); + parent::setCookie($name, $this->getClient()->hashCookieData($name, $val), $params); } /** @@ -866,8 +826,8 @@ public function setCookie($name, $val, $params = []) */ public function createAndSetCsrfCookie(string $val): array { - $masked = $this->client->maskToken($val); - $name = $this->client->getCsrfParamName(); + $masked = (new Security())->maskToken($val); + $name = $this->getClient()->getCsrfParamName(); $this->setCookie($name, $val); return [$name, $masked]; } @@ -885,7 +845,7 @@ public function _afterSuite(): void */ public function _initializeSession(): void { - $this->client->removeContext(); + $this->getClient()->restart(); $this->headers = []; $_SESSION = []; $_COOKIE = []; @@ -893,15 +853,15 @@ public function _initializeSession(): void /** * Return the session content for future restoring. Implements MultiSession. - * @return array backup data + * @return array backup data */ public function _backupSession(): array { - if (isset(Yii::$app) && Yii::$app->has('session', true) && Yii::$app->session->useCustomStorage) { + if (Yii::$app instanceof Application && Yii::$app->has('session', true) && Yii::$app->session->useCustomStorage) { throw new ModuleException($this, "Yii2 MultiSession only supports the default session backend."); } return [ - 'clientContext' => $this->client->getContext(), + 'clientContext' => $this->getClient()->getContext(), 'headers' => $this->headers, 'cookie' => isset($_COOKIE) ? $_COOKIE : [], 'session' => isset($_SESSION) ? $_SESSION : [], @@ -910,20 +870,21 @@ public function _backupSession(): array /** * Restore a session. Implements MultiSession. - * @param array output of _backupSession() + * @param array $session output of _backupSession() */ public function _loadSession($session): void { - $this->client->setContext($session['clientContext']); + $this->getClient()->setContext($session['clientContext']); $this->headers = $session['headers']; $_SESSION = $session['session']; $_COOKIE = $session['cookie']; // reset Yii::$app->user if (isset(Yii::$app)) { - $definitions = Yii::$app->getComponents(true); - if (Yii::$app->has('user', true)) { - Yii::$app->set('user', $definitions['user']); + $app = Yii::$app; + $definitions = $app->getComponents(true); + if ($app->has('user', true)) { + $app->set('user', $definitions['user']); } } } diff --git a/tests/Yii.stub b/tests/Yii.stub new file mode 100644 index 0000000..2a715d2 --- /dev/null +++ b/tests/Yii.stub @@ -0,0 +1,38 @@ +