diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f1065f909..f6dde2b97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,80 +1,32 @@ on: - - pull_request - - push + pull_request: + paths-ignore: + - 'docs/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - 'infection.json.dist' + - 'psalm.xml' + + push: + paths-ignore: + - 'docs/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - 'infection.json.dist' + - 'psalm.xml' name: build jobs: - tests: - name: PHP ${{ matrix.php }} - ${{ matrix.os }} - - env: + build: + uses: yiisoft/actions/.github/workflows/codeception.yml@master + with: extensions: dom, json, gd, imagick - key: cache-v1 - - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: - - ubuntu-latest - - windows-latest - - php: - - "7.4" - - "8.0" - - "8.1" - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup cache environment - id: cache-env - uses: shivammathur/cache-extensions@v1 - with: - php-version: ${{ matrix.php }} - extensions: ${{ env.extensions }} - key: ${{ env.key }} - - - name: Cache extensions - uses: actions/cache@v1 - with: - path: ${{ steps.cache-env.outputs.dir }} - key: ${{ steps.cache-env.outputs.key }} - restore-keys: ${{ steps.cache-env.outputs.key }} - - - name: Install PHP with extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: ${{ env.extensions }} - ini-values: date.timezone='UTC' - - - name: Determine composer cache directory on Linux - if: matrix.os == 'ubuntu-latest' - run: | - echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV - - - name: Determine composer cache directory on Windows - if: matrix.os == 'windows-latest' - run: | - echo "COMPOSER_CACHE_DIR=~\AppData\Local\Composer" >> $GITHUB_ENV - - - name: Cache dependencies installed with composer - uses: actions/cache@v1 - with: - path: ${{ steps.cache-env.outputs.dir }} - key: php${{ matrix.php }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }} - restore-keys: | - php${{ matrix.php }}-composer-${{ matrix.dependencies }}- - - - name: Install dependencies with composer php PHP [5.6 - 8.0] - run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - - - name: Run tests with codeception - run: | - sed -i "s/'cookieValidationKey' => ''/'cookieValidationKey' => 'testkey'/" config/web.php - php -S 127.0.0.1:8080 -t public > ./runtime/yii.log 2>&1 & - vendor/bin/codecept run - shell: bash + os: >- + ['ubuntu-latest', 'windows-latest'] + php: >- + ['8.1', '8.2'] \ No newline at end of file diff --git a/.gitignore b/.gitignore index 28419e445..277d796a0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,7 @@ tests/_output/* tests/_support/_generated #vagrant folder -/.vagrant \ No newline at end of file +/.vagrant + +#codeception +/c3.php \ No newline at end of file diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 000000000..fd87feb27 --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,44 @@ +checks: + php: true + +filter: + paths: + - "/*" + excluded_paths: + - "/config/*" + - "/runtime/*" + - "/tests/Support/*" + - "/vagrant/*" + - "/vendor/*" + +build: + image: default-bionic + + environment: + variables: + YII_ENV: test + + php: + version: 8.1.21 + ini: + xdebug.mode: coverage + + nodes: + analysis: + tests: + override: + - php-scrutinizer-run + + codeception: + dependencies: + override: + - composer self-update + - composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + tests: + override: + - command: vendor/bin/codecept run --coverage-xml + on_node: 1 + coverage: + file: runtime/_output/coverage.xml + format: php-clover \ No newline at end of file diff --git a/assets/AppAsset.php b/assets/AppAsset.php index 3d40487ae..e7038ab27 100644 --- a/assets/AppAsset.php +++ b/assets/AppAsset.php @@ -1,4 +1,7 @@ * @since 2.0 */ -class HelloController extends Controller +final class HelloController extends Controller { /** * This command echoes what you have entered as the message. + * * @param string $message the message to be echoed. + * * @return int Exit code */ - public function actionIndex($message = 'hello world') + public function actionIndex(string $message = 'hello world'): int { echo $message . "\n"; diff --git a/composer.json b/composer.json index 722fcba65..bf8685787 100644 --- a/composer.json +++ b/composer.json @@ -14,27 +14,30 @@ }, "minimum-stability": "dev", "require": { - "php": ">=7.4.0", - "yiisoft/yii2": "~2.0.45", + "php": ">=8.1", + "yiisoft/yii2": "2.2.x-dev as 2.0.48.1", "yiisoft/yii2-bootstrap5": "~2.0.2", "yiisoft/yii2-symfonymailer": "~2.0.3" }, "require-dev": { + "codeception/c3": "^2.8", + "codeception/codeception": "^5.0.0", + "codeception/lib-innerbrowser": "^4.0", + "codeception/module-asserts": "^3.0", + "codeception/module-filesystem": "^3.0", + "codeception/module-yii2": "^1.1", + "codeception/verify": "^3.0", + "maglnet/composer-require-checker": "^4.6", + "symfony/browser-kit": "^6.3", + "symfony/process": "^6.3", "yiisoft/yii2-debug": "~2.1.0", - "yiisoft/yii2-gii": "~2.2.0", "yiisoft/yii2-faker": "~2.0.0", - "phpunit/phpunit": "~9.5.0", - "codeception/codeception": "^5.0.0 || ^4.0", - "codeception/lib-innerbrowser": "^4.0 || ^3.0 || ^1.1", - "codeception/module-asserts": "^3.0 || ^1.1", - "codeception/module-yii2": "^1.1", - "codeception/module-filesystem": "^3.0 || ^2.0 || ^1.1", - "codeception/verify": "^3.0 || ^2.2", - "symfony/browser-kit": "^6.0 || >=2.7 <=4.2.4" + "yiisoft/yii2-gii": "~2.2.0" }, "config": { "allow-plugins": { - "yiisoft/yii2-composer" : true + "yiisoft/yii2-composer": true, + "codeception/c3": true }, "process-timeout": 1800, "fxp-asset": { diff --git a/config/web.php b/config/web.php index 8b9dd3f94..9ac0013f5 100644 --- a/config/web.php +++ b/config/web.php @@ -14,7 +14,7 @@ 'components' => [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation - 'cookieValidationKey' => '', + 'cookieValidationKey' => '1', ], 'cache' => [ 'class' => 'yii\caching\FileCache', diff --git a/controllers/SiteController.php b/controllers/SiteController.php index 67c3f50f8..cd3be2e7f 100644 --- a/controllers/SiteController.php +++ b/controllers/SiteController.php @@ -1,5 +1,7 @@ [ @@ -41,7 +43,7 @@ public function behaviors() /** * {@inheritdoc} */ - public function actions() + public function actions(): array { return [ 'error' => [ @@ -59,7 +61,7 @@ public function actions() * * @return string */ - public function actionIndex() + public function actionIndex(): string { return $this->render('index'); } @@ -69,7 +71,7 @@ public function actionIndex() * * @return Response|string */ - public function actionLogin() + public function actionLogin(): Response|string { if (!Yii::$app->user->isGuest) { return $this->goHome(); @@ -91,7 +93,7 @@ public function actionLogin() * * @return Response */ - public function actionLogout() + public function actionLogout(): Response { Yii::$app->user->logout(); @@ -103,7 +105,7 @@ public function actionLogout() * * @return Response|string */ - public function actionContact() + public function actionContact(): Response|string { $model = new ContactForm(); if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) { @@ -121,7 +123,7 @@ public function actionContact() * * @return string */ - public function actionAbout() + public function actionAbout(): string { return $this->render('about'); } diff --git a/models/ContactForm.php b/models/ContactForm.php index f001d2192..93caa0f8f 100644 --- a/models/ContactForm.php +++ b/models/ContactForm.php @@ -1,5 +1,7 @@ 'Verification Code', @@ -44,10 +46,12 @@ public function attributeLabels() /** * Sends an email to the specified email address using the information collected by this model. + * * @param string $email the target email address + * * @return bool whether the model passes validation */ - public function contact($email) + public function contact(string $email): bool { if ($this->validate()) { Yii::$app->mailer->compose() diff --git a/models/LoginForm.php b/models/LoginForm.php index dce15ccf7..86b1f5dfd 100644 --- a/models/LoginForm.php +++ b/models/LoginForm.php @@ -1,5 +1,7 @@ hasErrors()) { $user = $this->getUser(); @@ -55,9 +57,10 @@ public function validatePassword($attribute, $params) /** * Logs in a user using the provided username and password. + * * @return bool whether the user is logged in successfully */ - public function login() + public function login(): bool { if ($this->validate()) { return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0); @@ -68,14 +71,14 @@ public function login() /** * Finds user by [[username]] * - * @return User|null + * @return User|null the user identity */ - public function getUser() + public function getUser(): User|null { - if ($this->_user === false) { - $this->_user = User::findByUsername($this->username); + if ($this->user === false) { + $this->user = User::findByUsername($this->username); } - return $this->_user; + return $this->user; } } diff --git a/models/User.php b/models/User.php index 2e3fb25ed..fc285eeff 100644 --- a/models/User.php +++ b/models/User.php @@ -1,16 +1,20 @@ [ 'id' => '100', 'username' => 'admin', @@ -31,7 +35,7 @@ class User extends \yii\base\BaseObject implements \yii\web\IdentityInterface /** * {@inheritdoc} */ - public static function findIdentity($id) + public static function findIdentity($id): IdentityInterface|null { return isset(self::$users[$id]) ? new static(self::$users[$id]) : null; } @@ -39,7 +43,7 @@ public static function findIdentity($id) /** * {@inheritdoc} */ - public static function findIdentityByAccessToken($token, $type = null) + public static function findIdentityByAccessToken($token, $type = null): IdentityInterface|null { foreach (self::$users as $user) { if ($user['accessToken'] === $token) { @@ -53,13 +57,14 @@ public static function findIdentityByAccessToken($token, $type = null) /** * Finds user by username * - * @param string $username - * @return static|null + * @param string $username the username + * + * @return static|null the user object */ - public static function findByUsername($username) + public static function findByUsername($username): static|null { foreach (self::$users as $user) { - if (strcasecmp($user['username'], $username) === 0) { + if (strcasecmp((string) $user['username'], $username) === 0) { return new static($user); } } @@ -70,7 +75,7 @@ public static function findByUsername($username) /** * {@inheritdoc} */ - public function getId() + public function getId(): string { return $this->id; } @@ -78,7 +83,7 @@ public function getId() /** * {@inheritdoc} */ - public function getAuthKey() + public function getAuthKey(): string { return $this->authKey; } @@ -86,7 +91,7 @@ public function getAuthKey() /** * {@inheritdoc} */ - public function validateAuthKey($authKey) + public function validateAuthKey($authKey): bool { return $this->authKey === $authKey; } @@ -95,9 +100,10 @@ public function validateAuthKey($authKey) * Validates password * * @param string $password password to validate + * * @return bool if password provided is valid for current user */ - public function validatePassword($password) + public function validatePassword(string $password): bool { return $this->password === $password; } diff --git a/tests/Acceptance.suite.yml b/tests/Acceptance.suite.yml new file mode 100644 index 000000000..19fcf72af --- /dev/null +++ b/tests/Acceptance.suite.yml @@ -0,0 +1,17 @@ +# Codeception Test Suite Configuration +# +# Suite for acceptance tests. +# Perform tests in browser using the WebDriver or PhpBrowser. +# If you need both WebDriver and PHPBrowser tests - create a separate suite. + +actor: AcceptanceTester +extensions: + enabled: + - Codeception\Extension\RunProcess: + 0: php -d variables_order=EGPCS -S 127.0.0.1:8080 web/index-test.php -t web + sleep: 1 +modules: + enabled: + - Filesystem + - Yii2 + - Asserts diff --git a/tests/acceptance/AboutCest.php b/tests/Acceptance/AboutCest.php similarity index 52% rename from tests/acceptance/AboutCest.php rename to tests/Acceptance/AboutCest.php index 8a7be5af1..d2387c3f4 100644 --- a/tests/acceptance/AboutCest.php +++ b/tests/Acceptance/AboutCest.php @@ -1,10 +1,12 @@ amOnPage(Url::toRoute('/site/about')); $I->see('About', 'h1'); diff --git a/tests/acceptance/ContactCest.php b/tests/Acceptance/ContactCest.php similarity index 77% rename from tests/acceptance/ContactCest.php rename to tests/Acceptance/ContactCest.php index 90f98482e..abdbbd074 100644 --- a/tests/acceptance/ContactCest.php +++ b/tests/Acceptance/ContactCest.php @@ -1,21 +1,23 @@ amOnPage(Url::toRoute('/site/contact')); } - public function contactPageWorks(AcceptanceTester $I) + public function contactPageWorks(AcceptanceTester $I): void { $I->wantTo('ensure that contact page works'); $I->see('Contact', 'h1'); } - public function contactFormCanBeSubmitted(AcceptanceTester $I) + public function contactFormCanBeSubmitted(AcceptanceTester $I): void { $I->amGoingTo('submit contact form with correct data'); $I->fillField('#contactform-name', 'tester'); @@ -25,9 +27,8 @@ public function contactFormCanBeSubmitted(AcceptanceTester $I) $I->fillField('#contactform-verifycode', 'testme'); $I->click('contact-button'); - - $I->wait(2); // wait for button to be clicked + $I->expectTo('see message about successful sending'); $I->dontSeeElement('#contact-form'); $I->see('Thank you for contacting us. We will respond to you as soon as possible.'); } diff --git a/tests/acceptance/HomeCest.php b/tests/Acceptance/HomeCest.php similarity index 77% rename from tests/acceptance/HomeCest.php rename to tests/Acceptance/HomeCest.php index e65df16ae..8fd0610a8 100644 --- a/tests/acceptance/HomeCest.php +++ b/tests/Acceptance/HomeCest.php @@ -1,18 +1,20 @@ amOnPage(Url::toRoute('/site/index')); $I->see('My Company'); $I->seeLink('About'); $I->click('About'); - $I->wait(2); // wait for page to be opened + $I->expectTo('see About page'); $I->see('This is the About page.'); } } diff --git a/tests/acceptance/LoginCest.php b/tests/Acceptance/LoginCest.php similarity index 51% rename from tests/acceptance/LoginCest.php rename to tests/Acceptance/LoginCest.php index 6f5cb2f38..986700a09 100644 --- a/tests/acceptance/LoginCest.php +++ b/tests/Acceptance/LoginCest.php @@ -1,10 +1,12 @@ amOnPage(Url::toRoute('/site/login')); $I->see('Login', 'h1'); @@ -13,9 +15,18 @@ public function ensureThatLoginWorks(AcceptanceTester $I) $I->fillField('input[name="LoginForm[username]"]', 'admin'); $I->fillField('input[name="LoginForm[password]"]', 'admin'); $I->click('login-button'); - $I->wait(2); // wait for button to be clicked $I->expectTo('see user info'); $I->see('Logout'); + + $I->amGoingTo('try to login with the user is logged in'); + $I->amOnPage(Url::toRoute('/site/login')); + $I->dontSee('Login', 'h1'); + + $I->amGoingTo('logout'); + $I->click('Logout'); + + $I->expectTo('see home page'); + $I->see('Congratulations!'); } } diff --git a/tests/acceptance/_bootstrap.php b/tests/Acceptance/_bootstrap.php similarity index 100% rename from tests/acceptance/_bootstrap.php rename to tests/Acceptance/_bootstrap.php diff --git a/tests/functional.suite.yml b/tests/Functional.suite.yml similarity index 100% rename from tests/functional.suite.yml rename to tests/Functional.suite.yml diff --git a/tests/functional/ContactFormCest.php b/tests/Functional/ContactFormCest.php similarity index 81% rename from tests/functional/ContactFormCest.php rename to tests/Functional/ContactFormCest.php index d17ef52fd..a9c9750e5 100644 --- a/tests/functional/ContactFormCest.php +++ b/tests/Functional/ContactFormCest.php @@ -1,18 +1,20 @@ amOnRoute('site/contact'); } - public function openContactPage(\FunctionalTester $I) + public function openContactPage(FunctionalTester $I): void { $I->see('Contact', 'h1'); } - public function submitEmptyForm(\FunctionalTester $I) + public function submitEmptyForm(FunctionalTester $I): void { $I->submitForm('#contact-form', []); $I->expectTo('see validations errors'); @@ -24,7 +26,7 @@ public function submitEmptyForm(\FunctionalTester $I) $I->see('The verification code is incorrect'); } - public function submitFormWithIncorrectEmail(\FunctionalTester $I) + public function submitFormWithIncorrectEmail(FunctionalTester $I): void { $I->submitForm('#contact-form', [ 'ContactForm[name]' => 'tester', @@ -41,7 +43,7 @@ public function submitFormWithIncorrectEmail(\FunctionalTester $I) $I->dontSee('The verification code is incorrect', '.help-inline'); } - public function submitFormSuccessfully(\FunctionalTester $I) + public function submitFormSuccessfully(FunctionalTester $I): void { $I->submitForm('#contact-form', [ 'ContactForm[name]' => 'tester', diff --git a/tests/functional/LoginFormCest.php b/tests/Functional/LoginFormCest.php similarity index 69% rename from tests/functional/LoginFormCest.php rename to tests/Functional/LoginFormCest.php index 7a83a27d6..ce14808c0 100644 --- a/tests/functional/LoginFormCest.php +++ b/tests/Functional/LoginFormCest.php @@ -1,20 +1,21 @@ amOnRoute('site/login'); } - public function openLoginPage(\FunctionalTester $I) + public function openLoginPage(FunctionalTester $I): void { $I->see('Login', 'h1'); - } // demonstrates `amLoggedInAs` method - public function internalLoginById(\FunctionalTester $I) + public function internalLoginById(FunctionalTester $I): void { $I->amLoggedInAs(100); $I->amOnPage('/'); @@ -22,14 +23,14 @@ public function internalLoginById(\FunctionalTester $I) } // demonstrates `amLoggedInAs` method - public function internalLoginByInstance(\FunctionalTester $I) + public function internalLoginByInstance(FunctionalTester $I): void { $I->amLoggedInAs(\app\models\User::findByUsername('admin')); $I->amOnPage('/'); $I->see('Logout (admin)'); } - public function loginWithEmptyCredentials(\FunctionalTester $I) + public function loginWithEmptyCredentials(FunctionalTester $I): void { $I->submitForm('#login-form', []); $I->expectTo('see validations errors'); @@ -37,7 +38,7 @@ public function loginWithEmptyCredentials(\FunctionalTester $I) $I->see('Password cannot be blank.'); } - public function loginWithWrongCredentials(\FunctionalTester $I) + public function loginWithWrongCredentials(FunctionalTester $I): void { $I->submitForm('#login-form', [ 'LoginForm[username]' => 'admin', @@ -47,7 +48,7 @@ public function loginWithWrongCredentials(\FunctionalTester $I) $I->see('Incorrect username or password.'); } - public function loginSuccessfully(\FunctionalTester $I) + public function loginSuccessfully(FunctionalTester $I): void { $I->submitForm('#login-form', [ 'LoginForm[username]' => 'admin', diff --git a/tests/functional/_bootstrap.php b/tests/Functional/_bootstrap.php similarity index 100% rename from tests/functional/_bootstrap.php rename to tests/Functional/_bootstrap.php diff --git a/tests/_support/AcceptanceTester.php b/tests/Support/AcceptanceTester.php similarity index 80% rename from tests/_support/AcceptanceTester.php rename to tests/Support/AcceptanceTester.php index 4c7dcbb6d..c87742cbd 100644 --- a/tests/_support/AcceptanceTester.php +++ b/tests/Support/AcceptanceTester.php @@ -1,10 +1,12 @@ getScenario()->runStep(new \Codeception\Step\Condition('amInPath', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Opens a file and stores it's content. + * + * Usage: + * + * ``` php + * openFile('composer.json'); + * $I->seeInThisFile('codeception/codeception'); + * ``` + * @see \Codeception\Module\Filesystem::openFile() + */ + public function openFile(string $filename): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('openFile', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Deletes a file + * + * ``` php + * deleteFile('composer.lock'); + * ``` + * @see \Codeception\Module\Filesystem::deleteFile() + */ + public function deleteFile(string $filename): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('deleteFile', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Deletes directory with all subdirectories + * + * ``` php + * deleteDir('vendor'); + * ``` + * @see \Codeception\Module\Filesystem::deleteDir() + */ + public function deleteDir(string $dirname): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('deleteDir', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Copies directory with all contents + * + * ``` php + * copyDir('vendor','old_vendor'); + * ``` + * @see \Codeception\Module\Filesystem::copyDir() + */ + public function copyDir(string $src, string $dst): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('copyDir', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks If opened file has `text` in it. + * + * Usage: + * + * ``` php + * openFile('composer.json'); + * $I->seeInThisFile('codeception/codeception'); + * ``` + * @see \Codeception\Module\Filesystem::seeInThisFile() + */ + public function seeInThisFile(string $text): void { + $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInThisFile', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks If opened file has `text` in it. + * + * Usage: + * + * ``` php + * openFile('composer.json'); + * $I->seeInThisFile('codeception/codeception'); + * ``` + * @see \Codeception\Module\Filesystem::seeInThisFile() + */ + public function canSeeInThisFile(string $text): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInThisFile', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks If opened file has the `number` of new lines. + * + * Usage: + * + * ``` php + * openFile('composer.json'); + * $I->seeNumberNewLines(5); + * ``` + * + * @param int $number New lines + * @see \Codeception\Module\Filesystem::seeNumberNewLines() + */ + public function seeNumberNewLines(int $number): void { + $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeNumberNewLines', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks If opened file has the `number` of new lines. + * + * Usage: + * + * ``` php + * openFile('composer.json'); + * $I->seeNumberNewLines(5); + * ``` + * + * @param int $number New lines + * @see \Codeception\Module\Filesystem::seeNumberNewLines() + */ + public function canSeeNumberNewLines(int $number): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeNumberNewLines', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that contents of currently opened file matches $regex + * @see \Codeception\Module\Filesystem::seeThisFileMatches() + */ + public function seeThisFileMatches(string $regex): void { + $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeThisFileMatches', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks that contents of currently opened file matches $regex + * @see \Codeception\Module\Filesystem::seeThisFileMatches() + */ + public function canSeeThisFileMatches(string $regex): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeThisFileMatches', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks the strict matching of file contents. + * Unlike `seeInThisFile` will fail if file has something more than expected lines. + * Better to use with HEREDOC strings. + * Matching is done after removing "\r" chars from file content. + * + * ``` php + * openFile('process.pid'); + * $I->seeFileContentsEqual('3192'); + * ``` + * @see \Codeception\Module\Filesystem::seeFileContentsEqual() + */ + public function seeFileContentsEqual(string $text): void { + $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFileContentsEqual', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks the strict matching of file contents. + * Unlike `seeInThisFile` will fail if file has something more than expected lines. + * Better to use with HEREDOC strings. + * Matching is done after removing "\r" chars from file content. + * + * ``` php + * openFile('process.pid'); + * $I->seeFileContentsEqual('3192'); + * ``` + * @see \Codeception\Module\Filesystem::seeFileContentsEqual() + */ + public function canSeeFileContentsEqual(string $text): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFileContentsEqual', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks If opened file doesn't contain `text` in it + * + * ``` php + * openFile('composer.json'); + * $I->dontSeeInThisFile('codeception/codeception'); + * ``` + * @see \Codeception\Module\Filesystem::dontSeeInThisFile() + */ + public function dontSeeInThisFile(string $text): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('dontSeeInThisFile', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks If opened file doesn't contain `text` in it + * + * ``` php + * openFile('composer.json'); + * $I->dontSeeInThisFile('codeception/codeception'); + * ``` + * @see \Codeception\Module\Filesystem::dontSeeInThisFile() + */ + public function cantSeeInThisFile(string $text): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInThisFile', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Deletes a file + * @see \Codeception\Module\Filesystem::deleteThisFile() + */ + public function deleteThisFile(): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('deleteThisFile', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks if file exists in path. + * Opens a file when it's exists + * + * ``` php + * seeFileFound('UserModel.php','app/models'); + * ``` + * @see \Codeception\Module\Filesystem::seeFileFound() + */ + public function seeFileFound(string $filename, string $path = ""): void { + $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks if file exists in path. + * Opens a file when it's exists + * + * ``` php + * seeFileFound('UserModel.php','app/models'); + * ``` + * @see \Codeception\Module\Filesystem::seeFileFound() + */ + public function canSeeFileFound(string $filename, string $path = ""): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks if file does not exist in path + * @see \Codeception\Module\Filesystem::dontSeeFileFound() + */ + public function dontSeeFileFound(string $filename, string $path = ""): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('dontSeeFileFound', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks if file does not exist in path + * @see \Codeception\Module\Filesystem::dontSeeFileFound() + */ + public function cantSeeFileFound(string $filename, string $path = ""): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Erases directory contents + * + * ``` php + * cleanDir('logs'); + * ``` + * @see \Codeception\Module\Filesystem::cleanDir() + */ + public function cleanDir(string $dirname): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('cleanDir', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Saves contents to file + * @see \Codeception\Module\Filesystem::writeToFile() + */ + public function writeToFile(string $filename, string $contents): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('writeToFile', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Authenticates a user on a site without submitting a login form. + * Use it for fast pragmatic authorization in functional tests. + * + * ```php + * amLoggedInAs(1); + * + * // User object is passed as parameter + * $admin = \app\models\User::findByUsername('admin'); + * $I->amLoggedInAs($admin); + * ``` + * Requires the `user` component to be enabled and configured. + * + * @param $user + * @throws \Codeception\Exception\ModuleException + * @see \Codeception\Module\Yii2::amLoggedInAs() + */ + public function amLoggedInAs($user) { + return $this->getScenario()->runStep(new \Codeception\Step\Condition('amLoggedInAs', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Creates and loads fixtures from a config. + * The signature is the same as for the `fixtures()` method of `yii\test\FixtureTrait` + * + * ```php + * haveFixtures([ + * 'posts' => PostsFixture::class, + * 'user' => [ + * 'class' => UserFixture::class, + * 'dataFile' => '@tests/_data/models/user.php', + * ], + * ]); + * ``` + * + * Note: if you need to load fixtures before a test (probably before the + * cleanup transaction is started; `cleanup` option is `true` by default), + * you can specify the fixtures in the `_fixtures()` method of a test case + * + * ```php + * [ + * 'class' => UserFixture::class, + * 'dataFile' => codecept_data_dir() . 'user.php' + * ] + * ]; + * } + * ``` + * instead of calling `haveFixtures` in Cest `_before` + * + * @param $fixtures + * @part fixtures + * @see \Codeception\Module\Yii2::haveFixtures() + */ + public function haveFixtures($fixtures) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('haveFixtures', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Returns all loaded fixtures. + * Array of fixture instances + * + * @part fixtures + * @return array + * @see \Codeception\Module\Yii2::grabFixtures() + */ + public function grabFixtures() { + return $this->getScenario()->runStep(new \Codeception\Step\Action('grabFixtures', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Gets a fixture by name. + * Returns a Fixture instance. If a fixture is an instance of + * `\yii\test\BaseActiveFixture` a second parameter can be used to return a + * specific model: + * + * ```php + * haveFixtures(['users' => UserFixture::class]); + * + * $users = $I->grabFixture('users'); + * + * // get first user by key, if a fixture is an instance of ActiveFixture + * $user = $I->grabFixture('users', 'user1'); + * ``` + * + * @param $name + * @return mixed + * @throws \Codeception\Exception\ModuleException if the fixture is not found + * @part fixtures + * @see \Codeception\Module\Yii2::grabFixture() + */ + public function grabFixture($name, $index = NULL) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('grabFixture', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Inserts a record into the database. + * + * ``` php + * haveRecord('app\models\User', array('name' => 'Davert')); + * ?> + * ``` + * + * @param $model + * @param array $attributes + * @return mixed + * @part orm + * @see \Codeception\Module\Yii2::haveRecord() + */ + public function haveRecord($model, $attributes = []) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('haveRecord', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that a record exists in the database. + * + * ``` php + * $I->seeRecord('app\models\User', array('name' => 'davert')); + * ``` + * + * @param $model + * @param array $attributes + * @part orm + * @see \Codeception\Module\Yii2::seeRecord() + */ + public function seeRecord(string $model, array $attributes = []): void { + $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeRecord', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks that a record exists in the database. + * + * ``` php + * $I->seeRecord('app\models\User', array('name' => 'davert')); + * ``` + * + * @param $model + * @param array $attributes + * @part orm + * @see \Codeception\Module\Yii2::seeRecord() + */ + public function canSeeRecord(string $model, array $attributes = []): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeRecord', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that a record does not exist in the database. + * + * ``` php + * $I->dontSeeRecord('app\models\User', array('name' => 'davert')); + * ``` + * + * @param $model + * @param array $attributes + * @part orm + * @see \Codeception\Module\Yii2::dontSeeRecord() + */ + public function dontSeeRecord(string $model, array $attributes = []): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('dontSeeRecord', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks that a record does not exist in the database. + * + * ``` php + * $I->dontSeeRecord('app\models\User', array('name' => 'davert')); + * ``` + * + * @param $model + * @param array $attributes + * @part orm + * @see \Codeception\Module\Yii2::dontSeeRecord() + */ + public function cantSeeRecord(string $model, array $attributes = []): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeRecord', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Retrieves a record from the database + * + * ``` php + * $category = $I->grabRecord('app\models\User', array('name' => 'davert')); + * ``` + * + * @param $model + * @param array $attributes + * @return mixed + * @part orm + * @see \Codeception\Module\Yii2::grabRecord() + */ + public function grabRecord(string $model, array $attributes = []): mixed { + return $this->getScenario()->runStep(new \Codeception\Step\Action('grabRecord', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Similar to `amOnPage` but accepts a route as first argument and params as second + * + * ``` + * $I->amOnRoute('site/view', ['page' => 'about']); + * ``` + * + * @param string $route A route + * @param array $params Additional route parameters + * @see \Codeception\Module\Yii2::amOnRoute() + */ + public function amOnRoute(string $route, array $params = []): void { + $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnRoute', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Opens the page for the given relative URI or route. + * + * ``` php + * amOnPage('/'); + * // opens /register page + * $I->amOnPage('/register'); + * ``` + * + * @param string $page the page URI + * @see \Codeception\Module\Yii2::amOnPage() + */ + public function amOnPage(string $page): void { + $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnPage', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Gets a component from the Yii container. Throws an exception if the + * component is not available + * + * ```php + * grabComponent('mailer'); + * ``` + * + * @param $component + * @return mixed + * @throws \Codeception\Exception\ModuleException + * @deprecated in your tests you can use \Yii::$app directly. + * @see \Codeception\Module\Yii2::grabComponent() + */ + public function grabComponent(mixed $component) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('grabComponent', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that an email is sent. + * + * ```php + * seeEmailIsSent(); + * + * // check that only 3 emails were sent + * $I->seeEmailIsSent(3); + * ``` + * + * @param int $num + * @throws \Codeception\Exception\ModuleException + * @part email + * @see \Codeception\Module\Yii2::seeEmailIsSent() + */ + public function seeEmailIsSent(?int $num = NULL): void { + $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeEmailIsSent', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks that an email is sent. + * + * ```php + * seeEmailIsSent(); + * + * // check that only 3 emails were sent + * $I->seeEmailIsSent(3); + * ``` + * + * @param int $num + * @throws \Codeception\Exception\ModuleException + * @part email + * @see \Codeception\Module\Yii2::seeEmailIsSent() + */ + public function canSeeEmailIsSent(?int $num = NULL): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeEmailIsSent', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that no email was sent + * + * @part email + * @see \Codeception\Module\Yii2::dontSeeEmailIsSent() + */ + public function dontSeeEmailIsSent(): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('dontSeeEmailIsSent', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * [!] Conditional Assertion: Test won't be stopped on fail + * Checks that no email was sent + * + * @part email + * @see \Codeception\Module\Yii2::dontSeeEmailIsSent() + */ + public function cantSeeEmailIsSent(): void { + $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeEmailIsSent', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Returns array of all sent email messages. + * Each message implements the `yii\mail\MessageInterface` interface. + * Useful to perform additional checks using the `Asserts` module: + * + * ```php + * seeEmailIsSent(); + * $messages = $I->grabSentEmails(); + * $I->assertEquals('admin@site,com', $messages[0]->getTo()); + * ``` + * + * @part email + * @return array + * @throws \Codeception\Exception\ModuleException + * @see \Codeception\Module\Yii2::grabSentEmails() + */ + public function grabSentEmails(): array { + return $this->getScenario()->runStep(new \Codeception\Step\Action('grabSentEmails', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Returns the last sent email: + * + * ```php + * seeEmailIsSent(); + * $message = $I->grabLastSentEmail(); + * $I->assertEquals('admin@site,com', $message->getTo()); + * ``` + * @part email + * @see \Codeception\Module\Yii2::grabLastSentEmail() + */ + public function grabLastSentEmail(): object { + return $this->getScenario()->runStep(new \Codeception\Step\Action('grabLastSentEmail', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Returns a list of regex patterns for recognized domain names + * + * @return array + * @see \Codeception\Module\Yii2::getInternalDomains() + */ + public function getInternalDomains(): array { + return $this->getScenario()->runStep(new \Codeception\Step\Action('getInternalDomains', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * 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 array $params Additional cookie params like `domain`, `path`, `expires` and `secure`. + * @see \Codeception\Module\Yii2::setCookie() + */ + public function setCookie($name, $val, $params = []) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('setCookie', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Creates the CSRF Cookie. + * @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. + * @see \Codeception\Module\Yii2::createAndSetCsrfCookie() + */ + public function createAndSetCsrfCookie(string $val): array { + return $this->getScenario()->runStep(new \Codeception\Step\Action('createAndSetCsrfCookie', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Authenticates user for HTTP_AUTH + * @see \Codeception\Lib\InnerBrowser::amHttpAuthenticated() + */ + public function amHttpAuthenticated(string $username, string $password): void { + $this->getScenario()->runStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Sets the HTTP header to the passed value - which is used on + * subsequent HTTP requests through PhpBrowser. + * + * Example: + * ```php + * haveHttpHeader('X-Requested-With', 'Codeception'); + * $I->amOnPage('test-headers.php'); + * ``` + * + * To use special chars in Header Key use HTML Character Entities: + * Example: + * Header with underscore - 'Client_Id' + * should be represented as - 'Client_Id' or 'Client_Id' + * + * ```php + * haveHttpHeader('Client_Id', 'Codeception'); + * ``` + * + * @param string $name the name of the request header + * @param string $value the value to set it to for subsequent + * requests + * @see \Codeception\Lib\InnerBrowser::haveHttpHeader() + */ + public function haveHttpHeader(string $name, string $value): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('haveHttpHeader', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Deletes the header with the passed name. Subsequent requests + * will not have the deleted header in its request. + * + * Example: + * ```php + * haveHttpHeader('X-Requested-With', 'Codeception'); + * $I->amOnPage('test-headers.php'); + * // ... + * $I->deleteHeader('X-Requested-With'); + * $I->amOnPage('some-other-page.php'); + * ``` + * + * @param string $name the name of the header to delete. + * @see \Codeception\Lib\InnerBrowser::deleteHeader() + */ + public function deleteHeader(string $name): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('deleteHeader', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Perform a click on a link or a button, given by a locator. + * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. + * For buttons, the "value" attribute, "name" attribute, and inner text are searched. + * For links, the link text is searched. + * For images, the "alt" attribute and inner text of any parent links are searched. + * + * The second parameter is a context (CSS or XPath locator) to narrow the search. + * + * Note that if the locator matches a button of type `submit`, the form will be submitted. + * + * ``` php + * click('Logout'); + * // button of form + * $I->click('Submit'); + * // CSS button + * $I->click('#form input[type=submit]'); + * // XPath + * $I->click('//form/*[@type="submit"]'); + * // link in context + * $I->click('Logout', '#nav'); + * // using strict locator + * $I->click(['link' => 'Login']); + * ``` + * @param string|array $link + * @see \Codeception\Lib\InnerBrowser::click() + */ + public function click($link, $context = NULL): void { + $this->getScenario()->runStep(new \Codeception\Step\Action('click', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string (case insensitive). + * + * You can specify a specific HTML element (via CSS or XPath) as the second + * parameter to only search within that element. + * + * ``` php + * see('Logout'); // I can suppose user is logged in + * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page + * $I->see('Sign Up', '//body/h1'); // with XPath + * $I->see('Sign Up', ['css' => 'body h1']); // with strict CSS locator + * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->see('strong')` will return true for strings like: + * + * - `
I am Stronger than thou
` + * - `` + * + * But will *not* be true for strings like: + * + * - `Home` + * - `I am Stronger than thou
` + * - `` + * + * But will *not* be true for strings like: + * + * - `Home` + * - `I am Stronger than thou
` + * - `` + * + * But will ignore strings like: + * + * - `Home` + * - `I am Stronger than thou
` + * - `` + * + * But will ignore strings like: + * + * - `Home` + * - `