From 88ea36eea326a3f80eb3123de5721e8a9f4c640e Mon Sep 17 00:00:00 2001 From: Gintautas Miselis Date: Fri, 11 Mar 2022 16:55:16 +0200 Subject: [PATCH 1/3] Improve code style --- .github/workflows/static-analysis.yml | 28 ++ composer.json | 6 + phpcs.xml | 7 + src/Codeception/Constraint/WebDriver.php | 2 +- src/Codeception/Constraint/WebDriverNot.php | 2 +- src/Codeception/Module/WebDriver.php | 106 ++++--- tests/data/app/controllers.php | 263 +++++++++++------- tests/data/app/data.php | 16 +- tests/data/app/glue.php | 73 ++--- tests/data/app/index.php | 5 +- tests/data/app/view/form/bug3824.php | 6 +- tests/data/app/view/form/unchecked.php | 6 +- tests/data/app/view/index.php | 4 +- tests/data/app/view/info.php | 4 +- tests/data/app/view/search.php | 4 +- tests/phpcs.xml | 13 + tests/support/Step/RootWatcher.php | 3 +- tests/support/WebGuy.php | 4 +- tests/support/WebHelper.php | 3 +- tests/unit.suite.yml | 5 + ...ed_webelement.php => TestedWebElement.php} | 6 +- .../Constraints/WebDriverConstraintTest.php | 11 +- .../WebDriverNotConstraintTest.php | 11 +- tests/web/FriendWithStepsCept.php | 1 + tests/web/FriendsCept.php | 1 + tests/web/FriendsLeaveCept.php | 3 +- .../Module => web}/TestsForBrowsers.php | 2 +- .../Module => web}/TestsForWeb.php | 2 + tests/web/WebDriverTest.php | 62 ++--- tests/web/_steps/RootWatcher.php | 1 + 30 files changed, 417 insertions(+), 243 deletions(-) create mode 100644 .github/workflows/static-analysis.yml create mode 100644 phpcs.xml create mode 100644 tests/phpcs.xml create mode 100644 tests/unit.suite.yml rename tests/unit/Codeception/Constraints/{mocked_webelement.php => TestedWebElement.php} (79%) rename tests/{unit/Codeception/Module => web}/TestsForBrowsers.php (98%) rename tests/{unit/Codeception/Module => web}/TestsForWeb.php (99%) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 00000000..a2573428 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,28 @@ +name: Static analysis + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + phpcs: + name: Code style + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + ini-values: memory_limit=-1, date.timezone='UTC' + tools: phpcs + + - name: Check production code style + run: phpcs src/ + + - name: Check test code style + run: phpcs tests/ --standard=tests/phpcs.xml \ No newline at end of file diff --git a/composer.json b/composer.json index 7d151428..76cbdd6f 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,12 @@ "autoload":{ "classmap": ["src/"] }, + "autoload-dev": { + "classmap": [ + "tests/data/app/data.php", + "tests/unit/Codeception/Constraints/TestedWebElement.php" + ] + }, "config": { "classmap-authoritative": true } diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 00000000..122caf5c --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,7 @@ + + + Codeception code standard + + + + \ No newline at end of file diff --git a/src/Codeception/Constraint/WebDriver.php b/src/Codeception/Constraint/WebDriver.php index e6ec1541..9aee4b8d 100644 --- a/src/Codeception/Constraint/WebDriver.php +++ b/src/Codeception/Constraint/WebDriver.php @@ -54,7 +54,7 @@ protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure } $output = "Failed asserting that any element by " . Locator::humanReadableString($selector); - $output .= $this->uriMessage('on page'); + $output .= ' ' . $this->uriMessage('on page'); if (count($nodes) < 5) { $output .= "\nElements: "; diff --git a/src/Codeception/Constraint/WebDriverNot.php b/src/Codeception/Constraint/WebDriverNot.php index da25e379..5f62df27 100644 --- a/src/Codeception/Constraint/WebDriverNot.php +++ b/src/Codeception/Constraint/WebDriverNot.php @@ -38,7 +38,7 @@ protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure } $output = "There was {$selector} element"; - $output .= $this->uriMessage("on page"); + $output .= ' ' . $this->uriMessage('on page'); $output .= $this->nodesList($nodes, $this->string); $output .= "\ncontaining '{$this->string}'"; diff --git a/src/Codeception/Module/WebDriver.php b/src/Codeception/Module/WebDriver.php index 194055e2..a04fdec0 100644 --- a/src/Codeception/Module/WebDriver.php +++ b/src/Codeception/Module/WebDriver.php @@ -1,5 +1,6 @@ baseElement) { - throw new ModuleException($this, "Page not loaded. Use `\$I->amOnPage` (or hidden API methods `_request` and `_loadPage`) to open it"); + throw new ModuleException( + $this, + "Page not loaded. Use `\$I->amOnPage` (or hidden API methods `_request` and `_loadPage`) to open it" + ); } return $this->baseElement; @@ -468,7 +472,13 @@ protected function getBaseElement(): WebDriverSearchContext public function _initialize() { - $this->wdHost = sprintf('%s://%s:%s%s', $this->config['protocol'], $this->config['host'], $this->config['port'], $this->config['path']); + $this->wdHost = sprintf( + '%s://%s:%s%s', + $this->config['protocol'], + $this->config['host'], + $this->config['port'], + $this->config['path'] + ); $this->capabilities = $this->config['capabilities']; $this->capabilities[WebDriverCapabilityType::BROWSER_NAME] = $this->config['browser']; if ($proxy = $this->getProxy()) { @@ -674,7 +684,8 @@ public function debugWebDriverLogs(TestInterface $test = null): void $this->debugSection("Selenium {$logType} Logs", "\n" . $this->formatLogEntries($logEntries)); - if ($logType === 'browser' && $this->config['log_js_errors'] + if ( + $logType === 'browser' && $this->config['log_js_errors'] && ($test instanceof ScenarioDriven) ) { $this->logJSErrors($test, $logEntries); @@ -711,7 +722,8 @@ protected function formatLogEntries(array $logEntries): string protected function logJSErrors(ScenarioDriven $test, array $browserLogEntries): void { foreach ($browserLogEntries as $logEntry) { - if (isset($logEntry['level']) + if ( + isset($logEntry['level']) && isset($logEntry['message']) && $this->isJSError($logEntry['level'], $logEntry['message']) ) { @@ -1167,7 +1179,10 @@ public function click($link, $context = null): void try { $els = $this->match($page, $link); } catch (MalformedLocatorException $exception) { - throw new ElementNotFound("name={$link}", "'{$link}' is invalid CSS and XPath selector and Link or Button"); + throw new ElementNotFound( + "name={$link}", + "'{$link}' is invalid CSS and XPath selector and Link or Button" + ); } $el = reset($els); @@ -1237,7 +1252,6 @@ public function _findClickable(WebDriverSearchContext $page, $link): ?WebDriverE ".//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = {$locator} or ./@title = {$locator}]", ".//button[./@name = {$locator} or ./@title = {$locator}]" ); - $els = $page->findElements(WebDriverBy::xpath($xpath)); if (count($els) > 0) { return reset($els); @@ -1270,10 +1284,8 @@ protected function findFields($selector): array $locator = static::xPathLiteral(trim((string) $selector)); // by text or label $xpath = Locator::combine( - // @codingStandardsIgnoreStart ".//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = {$locator}) or ./@id = //label[contains(normalize-space(string(.)), {$locator})]/@for) or ./@placeholder = {$locator})]", ".//label[contains(normalize-space(string(.)), {$locator})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]" - // @codingStandardsIgnoreEnd ); $fields = $this->getBaseElement()->findElements(WebDriverBy::xpath($xpath)); if (!empty($fields)) { @@ -1321,7 +1333,10 @@ public function seeLink(string $text, string $url = null): void $nodes = $this->filterNodesByHref($url, $nodes); } - $this->assertNotEmpty($nodes, "No links containing text '{$text}' and URL '{$url}' were found in page {$currentUri}"); + $this->assertNotEmpty( + $nodes, + "No links containing text '{$text}' and URL '{$url}' were found in page {$currentUri}" + ); } public function dontSeeLink(string $text, string $url = ''): void @@ -1332,7 +1347,10 @@ public function dontSeeLink(string $text, string $url = ''): void $this->assertEmpty($nodes, "Link containing text '{$text}' was found in page {$currentUri}"); } else { $nodes = $this->filterNodesByHref($url, $nodes); - $this->assertEmpty($nodes, "Link containing text '{$text}' and URL '{$url}' was found in page {$currentUri}"); + $this->assertEmpty( + $nodes, + "Link containing text '{$text}' and URL '{$url}' was found in page {$currentUri}" + ); } } @@ -1529,15 +1547,15 @@ protected function proceedSeeInField(array $elements, $value): array break; case 'option': - // no break we need the trim text and the value also if (!$el->isSelected()) { break; } $currentValues[] = $el->getText(); + // no break we need the trim text and the value also case 'textarea': - // we include trimmed and real value of textarea for check $currentValues[] = trim($el->getText()); + // we include trimmed and real value of textarea for check default: $currentValues[] = $el->getAttribute('value'); // raw value break; @@ -1629,7 +1647,10 @@ public function selectOption($select, $option): void return; } - throw new ElementNotFound(json_encode($option, JSON_THROW_ON_ERROR), "Option inside {$select} matched by name or value"); + throw new ElementNotFound( + json_encode($option, JSON_THROW_ON_ERROR), + "Option inside {$select} matched by name or value" + ); } /** @@ -1662,7 +1683,10 @@ public function _initializeSession(): void $this->initialWindowSize(); } catch (WebDriverCurlException $exception) { codecept_debug('Curl error: ' . $exception->getMessage()); - throw new ConnectionException("Can't connect to WebDriver at {$this->wdHost}. Make sure that ChromeDriver, GeckoDriver or Selenium Server is running."); + throw new ConnectionException( + "Can't connect to WebDriver at {$this->wdHost}.'" + . ' Make sure that ChromeDriver, GeckoDriver or Selenium Server is running.' + ); } } @@ -1765,8 +1789,11 @@ public function unselectOption($select, $option): void /** * @param string|array|WebDriverBy|WebDriverElement $radioOrCheckbox */ - protected function findCheckable(WebDriverSearchContext $context, $radioOrCheckbox, bool $byValue = false): ?WebDriverElement - { + protected function findCheckable( + WebDriverSearchContext $context, + $radioOrCheckbox, + bool $byValue = false + ): ?WebDriverElement { if ($radioOrCheckbox instanceof WebDriverElement) { return $radioOrCheckbox; } @@ -1786,9 +1813,7 @@ protected function findCheckable(WebDriverSearchContext $context, $radioOrCheckb $typeLiteral = static::xPathLiteral($contextType); $inputLocatorFragment = "input[@type = {$typeLiteral}][@name = {$nameLiteral}]"; $xpath = Locator::combine( - // @codingStandardsIgnoreStart "ancestor::form//{$inputLocatorFragment}[(@id = ancestor::form//label[contains(normalize-space(string(.)), {$locator})]/@for) or @placeholder = {$locator}]", - // @codingStandardsIgnoreEnd "ancestor::form//label[contains(normalize-space(string(.)), {$locator})]//{$inputLocatorFragment}" ); if ($byValue) { @@ -1796,13 +1821,14 @@ protected function findCheckable(WebDriverSearchContext $context, $radioOrCheckb } } else { $xpath = Locator::combine( - // @codingStandardsIgnoreStart "//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), {$locator})]/@for) or @placeholder = {$locator} or @name = {$locator}]", - // @codingStandardsIgnoreEnd "//label[contains(normalize-space(string(.)), {$locator})]//input[@type = 'radio' or @type = 'checkbox']" ); if ($byValue) { - $xpath = Locator::combine($xpath, sprintf("//input[@type = 'checkbox' or @type = 'radio'][@value = %s]", $locator)); + $xpath = Locator::combine( + $xpath, + sprintf("//input[@type = 'checkbox' or @type = 'radio'][@value = %s]", $locator) + ); } } @@ -2446,7 +2472,9 @@ public function submitForm($selector, array $params, $button = null): void $form = $this->matchFirstOrFail($this->getBaseElement(), $selector); $fields = $form->findElements( - WebDriverBy::cssSelector('input:enabled[name],textarea:enabled[name],select:enabled[name],input[type=hidden][name]') + WebDriverBy::cssSelector( + 'input:enabled[name],textarea:enabled[name],select:enabled[name],input[type=hidden][name]' + ) ); foreach ($fields as $field) { $fieldName = $this->getSubmissionFormFieldName($field->getAttribute('name') ?? ''); @@ -3278,7 +3306,10 @@ public function appendField($field, string $value): void return; } - throw new ElementNotFound(json_encode($value, JSON_THROW_ON_ERROR), "Option inside {$field} matched by name or value"); + throw new ElementNotFound( + json_encode($value, JSON_THROW_ON_ERROR), + "Option inside {$field} matched by name or value" + ); case "textarea": $el->sendKeys($value); return; @@ -3491,7 +3522,7 @@ public function openNewTab(): void public function seeNumberOfTabs($number): void { $this->assertSame(count($this->webDriver->getWindowHandles()), $number); - } + } /** * Closes current browser tab and switches to previous active tab. @@ -3697,5 +3728,4 @@ private static function xPathLiteral($s): string return sprintf('concat(%s)', implode(', ', $parts)); } - } diff --git a/tests/data/app/controllers.php b/tests/data/app/controllers.php index 793e707a..f3306736 100755 --- a/tests/data/app/controllers.php +++ b/tests/data/app/controllers.php @@ -1,203 +1,253 @@ GET(); } } -class contentType1 { - function GET() { +class contentType1 +{ + function GET() + { header('Content-Type:', true); - include __DIR__.'/view/content_type.php'; + include __DIR__ . '/view/content_type.php'; } } -class contentType2 { - function GET() { +class contentType2 +{ + function GET() + { header('Content-Type:', true); - include __DIR__.'/view/content_type2.php'; + include __DIR__ . '/view/content_type2.php'; } } -class unsetCookie { - function GET() { +class unsetCookie +{ + function GET() + { header('Set-Cookie: a=; Expires=Thu, 01 Jan 1970 00:00:01 GMT'); } } -class basehref { - function GET() { - include __DIR__.'/view/basehref.php'; +class basehref +{ + function GET() + { + include __DIR__ . '/view/basehref.php'; } } -class jserroronload { - function GET() { - include __DIR__.'/view/jserroronload.php'; +class jserroronload +{ + function GET() + { + include __DIR__ . '/view/jserroronload.php'; } } -class userAgent { - function GET() { +class userAgent +{ + function GET() + { echo $_SERVER['HTTP_USER_AGENT']; } } -class minimal { - function GET() { - include __DIR__.'/view/minimal.php'; +class minimal +{ + function GET() + { + include __DIR__ . '/view/minimal.php'; } } diff --git a/tests/data/app/data.php b/tests/data/app/data.php index c1877c23..d7dca0e5 100755 --- a/tests/data/app/data.php +++ b/tests/data/app/data.php @@ -1,9 +1,11 @@ $class) { - $regex = str_replace('/', '\/', $regex); - $regex = '^' . $regex . '\/?$'; - if (preg_match("/$regex/i", $path, $matches)) { - $found = true; - if (class_exists($class)) { - $obj = new $class; - if (method_exists($obj, $method)) { - $obj->$method($matches); - } else { - throw new BadMethodCallException("Method, $method, not supported."); - } + foreach ($urls as $regex => $class) { + $regex = str_replace('/', '\/', $regex); + $regex = '^' . $regex . '\/?$'; + if (preg_match("/$regex/i", $path, $matches)) { + $found = true; + if (class_exists($class)) { + $obj = new $class(); + if (method_exists($obj, $method)) { + $obj->$method($matches); } else { - throw new Exception("Class, $class, not found."); + throw new BadMethodCallException("Method, $method, not supported."); } - break; + } else { + throw new Exception("Class, $class, not found."); } + break; } - if (!$found) { - throw new Exception("URL, $path, not found."); - } + } + if (!$found) { + throw new Exception("URL, $path, not found."); } } +} diff --git a/tests/data/app/index.php b/tests/data/app/index.php index 4c658042..c63e5c77 100755 --- a/tests/data/app/index.php +++ b/tests/data/app/index.php @@ -1,5 +1,8 @@ diff --git a/tests/data/app/view/form/unchecked.php b/tests/data/app/view/form/unchecked.php index 2e8848e2..63a7af25 100644 --- a/tests/data/app/view/form/unchecked.php +++ b/tests/data/app/view/form/unchecked.php @@ -8,9 +8,9 @@
diff --git a/tests/data/app/view/index.php b/tests/data/app/view/index.php index 38907595..5157fab8 100755 --- a/tests/data/app/view/index.php +++ b/tests/data/app/view/index.php @@ -4,7 +4,9 @@

Welcome to test app!

-
+

More info diff --git a/tests/data/app/view/info.php b/tests/data/app/view/info.php index b8041eff..65c2ea92 100755 --- a/tests/data/app/view/info.php +++ b/tests/data/app/view/info.php @@ -12,7 +12,9 @@ Back

-
+

Don't do that at home!

diff --git a/tests/data/app/view/search.php b/tests/data/app/view/search.php index 8c4de1d2..49a62c2b 100644 --- a/tests/data/app/view/search.php +++ b/tests/data/app/view/search.php @@ -5,7 +5,9 @@
- +
+ + Codeception code standard + + data/app + + + + + + + + \ No newline at end of file diff --git a/tests/support/Step/RootWatcher.php b/tests/support/Step/RootWatcher.php index 138579a9..7e06b5d9 100644 --- a/tests/support/Step/RootWatcher.php +++ b/tests/support/Step/RootWatcher.php @@ -1,4 +1,5 @@ amOnPage('/'); $I->see($message); } -} \ No newline at end of file +} diff --git a/tests/support/WebGuy.php b/tests/support/WebGuy.php index 62f29498..25120f21 100644 --- a/tests/support/WebGuy.php +++ b/tests/support/WebGuy.php @@ -1,6 +1,5 @@ constraint = new Codeception\PHPUnit\Constraint\WebDriver('hello', '/user'); + $this->constraint = new WebDriver('hello', '/user'); } public function testEvaluation() @@ -63,7 +62,7 @@ public function testFailMessageResponseWhenMoreNodes() public function testFailMessageResponseWithoutUrl() { - $this->constraint = new Codeception\PHPUnit\Constraint\WebDriver('hello'); + $this->constraint = new WebDriver('hello'); $nodes = array(new TestedWebElement('Bye warcraft'), new TestedWebElement('Bye world')); try { $this->constraint->evaluate($nodes, 'selector'); diff --git a/tests/unit/Codeception/Constraints/WebDriverNotConstraintTest.php b/tests/unit/Codeception/Constraints/WebDriverNotConstraintTest.php index 17e47748..d64eb855 100644 --- a/tests/unit/Codeception/Constraints/WebDriverNotConstraintTest.php +++ b/tests/unit/Codeception/Constraints/WebDriverNotConstraintTest.php @@ -2,20 +2,19 @@ declare(strict_types=1); -use Codeception\PHPUnit\Constraint\WebDriverNot; +namespace Tests\Unit\Codeception\Constraints; + +use Codeception\Constraint\WebDriverNot; use Codeception\PHPUnit\TestCase; use Codeception\Util\Locator; -require_once __DIR__.'/mocked_webelement.php'; - class WebDriverConstraintNotTest extends TestCase { - protected ?WebDriverNot $constraint = null; public function _setUp() { - $this->constraint = new Codeception\PHPUnit\Constraint\WebDriverNot('warcraft', '/user'); + $this->constraint = new WebDriverNot('warcraft', '/user'); } public function testEvaluation() @@ -70,7 +69,7 @@ public function testFailMessageResponseWhenMoreNodes() public function testFailMessageResponseWithoutUrl() { - $this->constraint = new Codeception\PHPUnit\Constraint\WebDriverNot('warcraft'); + $this->constraint = new WebDriverNot('warcraft'); $nodes = array(new TestedWebElement('Bye warcraft'), new TestedWebElement('Bye world')); try { $this->constraint->evaluate($nodes, 'selector'); diff --git a/tests/web/FriendWithStepsCept.php b/tests/web/FriendWithStepsCept.php index 8e1de9b8..2b04b6bf 100644 --- a/tests/web/FriendWithStepsCept.php +++ b/tests/web/FriendWithStepsCept.php @@ -1,4 +1,5 @@ wantTo('call friend with steps to ask expert work'); $I->amOnPage('/info'); diff --git a/tests/web/FriendsCept.php b/tests/web/FriendsCept.php index 0fd071cd..cdb8ca17 100644 --- a/tests/web/FriendsCept.php +++ b/tests/web/FriendsCept.php @@ -1,4 +1,5 @@ wantTo('call friends to try multi session'); $I->amOnPage('/info'); diff --git a/tests/web/FriendsLeaveCept.php b/tests/web/FriendsLeaveCept.php index 5af89f23..f931ece4 100644 --- a/tests/web/FriendsLeaveCept.php +++ b/tests/web/FriendsLeaveCept.php @@ -1,4 +1,5 @@ wantTo('test leave bug'); $I->amOnPage('/info'); @@ -11,4 +12,4 @@ $I->see('Information', 'h1'); $jon->leave(); -$I->see('Don\'t do that at home'); \ No newline at end of file +$I->see('Don\'t do that at home'); diff --git a/tests/unit/Codeception/Module/TestsForBrowsers.php b/tests/web/TestsForBrowsers.php similarity index 98% rename from tests/unit/Codeception/Module/TestsForBrowsers.php rename to tests/web/TestsForBrowsers.php index 381856a3..d4be59e9 100644 --- a/tests/unit/Codeception/Module/TestsForBrowsers.php +++ b/tests/web/TestsForBrowsers.php @@ -2,7 +2,7 @@ declare(strict_types=1); -require_once 'TestsForWeb.php'; +namespace Tests\Web; /** * Author: davert diff --git a/tests/unit/Codeception/Module/TestsForWeb.php b/tests/web/TestsForWeb.php similarity index 99% rename from tests/unit/Codeception/Module/TestsForWeb.php rename to tests/web/TestsForWeb.php index 89510b79..76d790cf 100644 --- a/tests/unit/Codeception/Module/TestsForWeb.php +++ b/tests/web/TestsForWeb.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace Tests\Web; + use Codeception\Module\WebDriver; use Codeception\Test\Unit; use Codeception\Util\ActionSequence; diff --git a/tests/web/WebDriverTest.php b/tests/web/WebDriverTest.php index 7a1fbc72..f8413fa2 100644 --- a/tests/web/WebDriverTest.php +++ b/tests/web/WebDriverTest.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace Tests\Web; + use Codeception\Module\WebDriver; use Codeception\Stub; use Codeception\Stub\Expected; @@ -13,9 +15,6 @@ use Facebook\WebDriver\WebDriverKeys; use PHPUnit\Framework\Assert; -require_once codecept_data_dir() . 'app/data.php'; -require_once __DIR__ . '/../unit/Codeception/Module/TestsForBrowsers.php'; - final class WebDriverTest extends TestsForBrowsers { protected ?WebDriver $module; @@ -125,42 +124,42 @@ public function testFailedDontSeeInPopup() public function testScreenshot() { $this->module->amOnPage('/'); - @unlink(\Codeception\Configuration::outputDir().'testshot.png'); - $testName="debugTest"; + @unlink(\Codeception\Configuration::outputDir() . 'testshot.png'); + $testName = "debugTest"; $this->module->makeScreenshot($testName); - $this->assertFileExists(\Codeception\Configuration::outputDir().'debug/'.$testName.'.png'); - @unlink(\Codeception\Configuration::outputDir().'debug/'.$testName.'.png'); + $this->assertFileExists(\Codeception\Configuration::outputDir() . 'debug/' . $testName . '.png'); + @unlink(\Codeception\Configuration::outputDir() . 'debug/' . $testName . '.png'); - $this->module->_saveScreenshot(\Codeception\Configuration::outputDir().'testshot.png'); - $this->assertFileExists(\Codeception\Configuration::outputDir().'testshot.png'); - @unlink(\Codeception\Configuration::outputDir().'testshot.png'); + $this->module->_saveScreenshot(\Codeception\Configuration::outputDir() . 'testshot.png'); + $this->assertFileExists(\Codeception\Configuration::outputDir() . 'testshot.png'); + @unlink(\Codeception\Configuration::outputDir() . 'testshot.png'); } public function testElementScreenshot() { $this->module->amOnPage('/'); - @unlink(\Codeception\Configuration::outputDir().'testelementshot.png'); - $testName="debugTestElement"; + @unlink(\Codeception\Configuration::outputDir() . 'testelementshot.png'); + $testName = "debugTestElement"; $this->module->makeElementScreenshot('#area4', $testName); - $this->assertFileExists(\Codeception\Configuration::outputDir().'debug/'.$testName.'.png'); - @unlink(\Codeception\Configuration::outputDir().'debug/'.$testName.'.png'); + $this->assertFileExists(\Codeception\Configuration::outputDir() . 'debug/' . $testName . '.png'); + @unlink(\Codeception\Configuration::outputDir() . 'debug/' . $testName . '.png'); - $this->module->_saveElementScreenshot('#area4', \Codeception\Configuration::outputDir().'testshot.png'); - $this->assertFileExists(\Codeception\Configuration::outputDir().'testshot.png'); - @unlink(\Codeception\Configuration::outputDir().'testelementshot.png'); + $this->module->_saveElementScreenshot('#area4', \Codeception\Configuration::outputDir() . 'testshot.png'); + $this->assertFileExists(\Codeception\Configuration::outputDir() . 'testshot.png'); + @unlink(\Codeception\Configuration::outputDir() . 'testelementshot.png'); } public function testSnapshot() { $this->module->amOnPage('/'); - @unlink(\Codeception\Configuration::outputDir().'testshot.png'); - $testName="debugTest"; + @unlink(\Codeception\Configuration::outputDir() . 'testshot.png'); + $testName = "debugTest"; $this->module->makeHtmlSnapshot($testName); - $this->assertFileExists(\Codeception\Configuration::outputDir().'debug/'.$testName.'.html'); - @unlink(\Codeception\Configuration::outputDir().'debug/'.$testName.'.html'); + $this->assertFileExists(\Codeception\Configuration::outputDir() . 'debug/' . $testName . '.html'); + @unlink(\Codeception\Configuration::outputDir() . 'debug/' . $testName . '.html'); } /** @@ -329,7 +328,7 @@ public function testWait() $this->module->amOnPage('/'); $time = time(); $this->module->wait(3); - $this->assertGreaterThanOrEqual($time+3, time()); + $this->assertGreaterThanOrEqual($time + 3, time()); } @@ -402,7 +401,6 @@ public function testTypeOnTextField() $this->module->click('Submit'); $form = data::get('form'); $this->assertSame('Hello world', $form['name']); - } public function testAppendFieldTextFails() @@ -592,7 +590,7 @@ public function testCreateTestScreenshotOnFail() $fakeWd = Stub::make(self::WEBDRIVER_CLASS, [ 'takeScreenshot' => Expected::once(function ($filename) use ($test) { Assert::assertSame( - codecept_log_dir(get_class($test).'.testLogin.fail.png'), + codecept_log_dir(get_class($test) . '.testLogin.fail.png'), $filename ); }), @@ -693,7 +691,7 @@ public function testBug1637() public function testBug2046() { $this->module->webDriver = null; - $this->module->_saveScreenshot(\Codeception\Configuration::outputDir().'testshot.png'); + $this->module->_saveScreenshot(\Codeception\Configuration::outputDir() . 'testshot.png'); } public function testSessionSnapshots() @@ -1061,8 +1059,7 @@ public function testPerformOnWithBuiltArray() $this->module->performOn('.rememberMe', \Codeception\Util\ActionSequence::build() ->see('Remember me next time') ->seeElement('#LoginForm_rememberMe') - ->dontSee('Login') - ); + ->dontSee('Login')); $this->assertSame(3, Assert::getCount() - $asserts); $this->module->see('Login'); } @@ -1074,8 +1071,7 @@ public function testPerformOnWithArrayAndSimilarActions() $this->module->performOn('.rememberMe', \Codeception\Util\ActionSequence::build() ->see('Remember me') ->see('next time') - ->dontSee('Login') - ); + ->dontSee('Login')); $this->assertSame(3, Assert::getCount() - $asserts); $this->module->see('Login'); } @@ -1086,8 +1082,7 @@ public function testPerformOnFail() $this->module->amOnPage('/form/example1'); $this->module->performOn('.rememberMe', \Codeception\Util\ActionSequence::build() ->seeElement('#LoginForm_rememberMe') - ->see('Remember me tomorrow') - ); + ->see('Remember me tomorrow')); } public function testPerformOnFail2() @@ -1110,7 +1105,6 @@ public function testSwitchToIframe() $this->module->see('Iframe test'); $this->module->switchToIFrame("//iframe[@name='content']"); $this->module->see('Lots of valuable data here'); - } /** @@ -1128,7 +1122,7 @@ public function testGrabPageSourceWhenOnPage() { $this->module->amOnPage('/minimal'); $sourceExpected = -<< @@ -1156,7 +1150,7 @@ public function testChangingCapabilities() $this->notForPhantomJS(); $this->assertNotTrue($this->module->webDriver->getCapabilities()->getCapability('acceptInsecureCerts')); $this->module->_closeSession(); - $this->module->_capabilities(function($current) { + $this->module->_capabilities(function ($current) { $current['acceptInsecureCerts'] = true; return new DesiredCapabilities($current); }); diff --git a/tests/web/_steps/RootWatcher.php b/tests/web/_steps/RootWatcher.php index d65971ca..0e60fb8f 100644 --- a/tests/web/_steps/RootWatcher.php +++ b/tests/web/_steps/RootWatcher.php @@ -1,4 +1,5 @@ Date: Fri, 11 Mar 2022 16:56:36 +0200 Subject: [PATCH 2/3] Fix Page constraint name --- src/Codeception/Module/WebDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codeception/Module/WebDriver.php b/src/Codeception/Module/WebDriver.php index a04fdec0..fef6258d 100644 --- a/src/Codeception/Module/WebDriver.php +++ b/src/Codeception/Module/WebDriver.php @@ -23,7 +23,7 @@ use Codeception\Lib\Interfaces\SessionSnapshot; use Codeception\Lib\Interfaces\Web as WebInterface; use Codeception\Module as CodeceptionModule; -use Codeception\PHPUnit\Constraint\Page as PageConstraint; +use Codeception\Constraint\Page as PageConstraint; use Codeception\Constraint\WebDriver as WebDriverConstraint; use Codeception\Constraint\WebDriverNot as WebDriverConstraintNot; use Codeception\Test\Descriptor; From a84cbb1132314349e95389c0e70777cf6b5e425b Mon Sep 17 00:00:00 2001 From: Gintautas Miselis Date: Fri, 11 Mar 2022 18:46:51 +0200 Subject: [PATCH 3/3] Fix some issues reported by phpstan --- .github/workflows/static-analysis.yml | 19 +++++++++-- composer.json | 5 ++- src/Codeception/Module/WebDriver.php | 46 +++++++++++---------------- tests/web/TestsForWeb.php | 1 + tests/web/WebDriverTest.php | 3 +- 5 files changed, 43 insertions(+), 31 deletions(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index a2573428..c42c78c0 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -19,10 +19,25 @@ jobs: with: php-version: '8.1' ini-values: memory_limit=-1, date.timezone='UTC' - tools: phpcs + tools: phpcs, phpstan + + - name: Validate composer.json + run: composer validate + + - name: Install dependencies + run: composer update + + - name: Generate action files + run: vendor/bin/codecept build - name: Check production code style run: phpcs src/ - name: Check test code style - run: phpcs tests/ --standard=tests/phpcs.xml \ No newline at end of file + run: phpcs tests/ --standard=tests/phpcs.xml + + - name: Static analysis of production code + run: phpstan analyze src/ --level=1 + + - name: Static analysis of test code + run: phpstan analyze tests/ diff --git a/composer.json b/composer.json index 76cbdd6f..c390bfa3 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,10 @@ "classmap": [ "tests/data/app/data.php", "tests/unit/Codeception/Constraints/TestedWebElement.php" - ] + ], + "psr-4": { + "Tests\\Web\\": "tests/web/" + } }, "config": { "classmap-authoritative": true diff --git a/src/Codeception/Module/WebDriver.php b/src/Codeception/Module/WebDriver.php index fef6258d..7c644f81 100644 --- a/src/Codeception/Module/WebDriver.php +++ b/src/Codeception/Module/WebDriver.php @@ -984,7 +984,7 @@ private function debugCookies(): void $result = []; $cookies = $this->webDriver->manage()->getCookies(); foreach ($cookies as $cookie) { - $result[] = is_array($cookie) ? $cookie : $cookie->toArray(); + $result[] = $cookie->toArray(); } $this->debugSection('Cookies', json_encode($result, JSON_THROW_ON_ERROR)); @@ -1228,7 +1228,7 @@ public function _findClickable(WebDriverSearchContext $page, $link): ?WebDriverE return $this->matchFirstOrFail($page, $link); } - $locator = static::xPathLiteral(trim((string) $link)); + $locator = self::xPathLiteral(trim((string) $link)); // narrow $xpath = Locator::combine( @@ -1281,7 +1281,7 @@ protected function findFields($selector): array return $fields; } - $locator = static::xPathLiteral(trim((string) $selector)); + $locator = self::xPathLiteral(trim((string) $selector)); // by text or label $xpath = Locator::combine( ".//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = {$locator}) or ./@id = //label[contains(normalize-space(string(.)), {$locator})]/@for) or ./@placeholder = {$locator})]", @@ -1354,7 +1354,7 @@ public function dontSeeLink(string $text, string $url = ''): void } } - private function filterNodesByHref(string $url, array $nodes): ?array + private function filterNodesByHref(string $url, array $nodes): array { //current uri can be relative, merging it with configured base url gives absolute url $absoluteCurrentUrl = Uri::mergeUrls($this->_getUrl(), $this->_getCurrentUri()); @@ -1802,15 +1802,15 @@ protected function findCheckable( return $this->matchFirstOrFail($this->getBaseElement(), $radioOrCheckbox); } - $locator = static::xPathLiteral($radioOrCheckbox); + $locator = self::xPathLiteral($radioOrCheckbox); if ($context instanceof WebDriverElement && $context->getTagName() === 'input') { $contextType = $context->getAttribute('type'); if (!in_array($contextType, ['checkbox', 'radio'], true)) { return null; } - $nameLiteral = static::xPathLiteral($context->getAttribute('name')); - $typeLiteral = static::xPathLiteral($contextType); + $nameLiteral = self::xPathLiteral($context->getAttribute('name')); + $typeLiteral = self::xPathLiteral($contextType); $inputLocatorFragment = "input[@type = {$typeLiteral}][@name = {$nameLiteral}]"; $xpath = Locator::combine( "ancestor::form//{$inputLocatorFragment}[(@id = ancestor::form//label[contains(normalize-space(string(.)), {$locator})]/@for) or @placeholder = {$locator}]", @@ -2221,11 +2221,9 @@ public function cancelPopup(): void * Checks that the active JavaScript popup, * as created by `window.alert`|`window.confirm`|`window.prompt`, contains the given string. * - * @param $text - * * @throws ModuleException */ - public function seeInPopup($text): void + public function seeInPopup(string $text): void { if ($this->isPhantom()) { throw new ModuleException($this, 'PhantomJS does not support working with popups'); @@ -2264,10 +2262,9 @@ public function dontSeeInPopup(string $text): void /** * Enters text into a native JavaScript prompt popup, as created by `window.prompt`. * - * @param $keys * @throws ModuleException */ - public function typeInPopup($keys): void + public function typeInPopup(string $keys): void { if ($this->isPhantom()) { throw new ModuleException($this, 'PhantomJS does not support working with popups'); @@ -2883,11 +2880,10 @@ public function waitForJS(string $script, int $timeout = 5): void * $I->executeJS("window.alert(arguments[0])", ['Hello world']); * ``` * - * @param $script * @param array $arguments * @return mixed */ - public function executeJS($script, array $arguments = []) + public function executeJS(string $script, array $arguments = []) { return $this->webDriver->executeScript($script, $arguments); } @@ -2906,11 +2902,10 @@ public function executeJS($script, array $arguments = []) * $I->executeAsyncJS('setTimeout(arguments[1], arguments[0])', [$seconds]); * ``` * - * @param $script * @param array $arguments * @return mixed */ - public function executeAsyncJS($script, array $arguments = []) + public function executeAsyncJS(string $script, array $arguments = []) { return $this->webDriver->executeAsyncScript($script, $arguments); } @@ -2934,7 +2929,7 @@ public function maximizeWindow(): void * @param string|array|WebDriverBy $source (CSS ID or XPath) * @param string|array|WebDriverBy $target (CSS ID or XPath) */ - public function dragAndDrop(string $source, string $target): void + public function dragAndDrop($source, $target): void { $sourceNodes = $this->matchFirstOrFail($this->getBaseElement(), $source); $targetNodes = $this->matchFirstOrFail($this->getBaseElement(), $target); @@ -3123,7 +3118,7 @@ protected function getStrictLocator(array $by): WebDriverBy return WebDriverBy::className($locator); default: throw new MalformedLocatorException( - "{$by} => {$locator}", + "{$type} => {$locator}", "Strict locator can be either xpath, css, id, link, class, name: " ); } @@ -3440,10 +3435,8 @@ public function deleteSessionSnapshot($name) * Check if the cookie domain matches the config URL. * * Taken from Guzzle\Cookie\SetCookie - * - * @param array|WebDriverCookie $cookie */ - private function cookieDomainMatchesConfigUrl(WebDriverCookie $cookie): bool + private function cookieDomainMatchesConfigUrl(array|WebDriverCookie $cookie): bool { if (!isset($cookie['domain'])) { return true; @@ -3517,11 +3510,10 @@ public function openNewTab(): void * seeNumberOfTabs(2); * ``` - * @param $number number of tabs */ - public function seeNumberOfTabs($number): void + public function seeNumberOfTabs(int $number): void { - $this->assertSame(count($this->webDriver->getWindowHandles()), $number); + $this->assertCount($number, $this->webDriver->getWindowHandles()); } /** @@ -3692,13 +3684,13 @@ protected function disableImplicitWait(): void * * Examples: * - * echo static::xpathLiteral('foo " bar'); + * echo self::xPathLiteral('foo " bar'); * //prints 'foo " bar' * - * echo static::xpathLiteral("foo ' bar"); + * echo self::xPathLiteral("foo ' bar"); * //prints "foo ' bar" * - * echo static::xpathLiteral('a\'b"c'); + * echo self::xPathLiteral('a\'b"c'); * //prints concat('a', "'", 'b"c') * * @return string Converted string diff --git a/tests/web/TestsForWeb.php b/tests/web/TestsForWeb.php index 76d790cf..6679a667 100644 --- a/tests/web/TestsForWeb.php +++ b/tests/web/TestsForWeb.php @@ -7,6 +7,7 @@ use Codeception\Module\WebDriver; use Codeception\Test\Unit; use Codeception\Util\ActionSequence; +use data; /** * Author: davert diff --git a/tests/web/WebDriverTest.php b/tests/web/WebDriverTest.php index f8413fa2..1aa406c6 100644 --- a/tests/web/WebDriverTest.php +++ b/tests/web/WebDriverTest.php @@ -8,6 +8,7 @@ use Codeception\Stub; use Codeception\Stub\Expected; use Codeception\Util\Maybe; +use data; use Facebook\WebDriver\Cookie; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Remote\RemoteWebDriver; @@ -580,7 +581,7 @@ public function testCreateCestScreenshotOnFail() ]), ]); $module = Stub::make(self::MODULE_CLASS, ['webDriver' => $fakeWd]); - $cest = new \Codeception\Test\Cest(new stdClass(), 'login', 'someCest.php'); + $cest = new \Codeception\Test\Cest(new \stdClass(), 'login', 'someCest.php'); $module->_failed($cest, new \PHPUnit\Framework\AssertionFailedError()); }