From 29391610ff4d70bdff340970af0b9de12a34ad74 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sat, 16 Mar 2024 22:07:35 +0100 Subject: [PATCH 01/25] Console output: Add space before time (#6744) --- src/Codeception/Subscriber/Console.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codeception/Subscriber/Console.php b/src/Codeception/Subscriber/Console.php index 5d229ed206..e20494fe7b 100644 --- a/src/Codeception/Subscriber/Console.php +++ b/src/Codeception/Subscriber/Console.php @@ -811,7 +811,7 @@ protected function writeTimeInformation(TestEvent $event): void if ($time !== 0.0) { $this ->message(number_format(round($time, 2), 2)) - ->prepend('(') + ->prepend(' (') ->append('s)') ->style('info') ->write(); From e4ed4573137ebff2f5a752d89a6b6eb3129d6361 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Thu, 16 May 2024 18:46:30 +0200 Subject: [PATCH 02/25] Improved error message about missing Codeception module --- src/Codeception/Lib/ModuleContainer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codeception/Lib/ModuleContainer.php b/src/Codeception/Lib/ModuleContainer.php index 9dd2bb1eec..bda5655dc0 100644 --- a/src/Codeception/Lib/ModuleContainer.php +++ b/src/Codeception/Lib/ModuleContainer.php @@ -97,7 +97,7 @@ public function create(string $moduleName, bool $active = true): ?object if (!class_exists($moduleClass)) { if (isset(self::$packages[$moduleName])) { $package = self::$packages[$moduleName]; - throw new ConfigurationException("Module {$moduleName} is not installed.\nUse Composer to install corresponding package:\n\ncomposer require {$package} --dev"); + throw new ConfigurationException("Codeception's module {$moduleName} not found. Install it with:\n\ncomposer require {$package} --dev"); } throw new ConfigurationException("Module {$moduleName} could not be found and loaded"); } From 0297b0b030b05a3163bbb80153e0d5e0a234bae0 Mon Sep 17 00:00:00 2001 From: Anton Volokha Date: Thu, 16 May 2024 19:56:36 +0300 Subject: [PATCH 03/25] Remove FAIL message color highlighting (#6754) --- src/Codeception/Reporter/ReportPrinter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codeception/Reporter/ReportPrinter.php b/src/Codeception/Reporter/ReportPrinter.php index 580c698494..9042c29cbf 100644 --- a/src/Codeception/Reporter/ReportPrinter.php +++ b/src/Codeception/Reporter/ReportPrinter.php @@ -64,7 +64,7 @@ public function testError(FailEvent $event): void public function testFailure(FailEvent $event): void { - $this->printTestResult($event->getTest(), "\033[41;37mFAIL\033[0m"); + $this->printTestResult($event->getTest(), "FAIL"); $this->failureCount++; } From 17b6765d210a8386af60da84bb55c0e329984a6f Mon Sep 17 00:00:00 2001 From: Aaron Gustavo Nieves <64917965+TavoNiievez@users.noreply.github.com> Date: Tue, 28 May 2024 21:14:38 -0500 Subject: [PATCH 04/25] Update the codebase to PHP 8.1 (#6747) * PHP 8.0 * CI: Run tests on PHP 8.3 * PHP 8.1 * tests & ext * Fix PR comments --- .github/workflows/build.yml | 20 +++--- composer.json | 2 +- ext/Recorder.php | 28 ++++---- ext/RunBefore.php | 2 +- ext/RunFailed.php | 2 +- ext/RunProcess.php | 5 +- src/Codeception/Actor.php | 2 +- src/Codeception/Application.php | 4 +- src/Codeception/Codecept.php | 18 ++--- src/Codeception/Command/Build.php | 4 +- src/Codeception/Command/DryRun.php | 6 +- src/Codeception/Command/GenerateFeature.php | 4 +- src/Codeception/Command/GenerateScenarios.php | 4 +- .../Command/GenerateStepObject.php | 7 +- src/Codeception/Command/GherkinSnippets.php | 2 +- src/Codeception/Command/Run.php | 12 ++-- src/Codeception/Command/Shared/ActorTrait.php | 2 +- src/Codeception/Configuration.php | 8 +-- src/Codeception/Coverage/Filter.php | 28 ++++---- .../Coverage/PhpCodeCoverageFactory.php | 2 +- .../Coverage/Subscriber/LocalServer.php | 11 +-- src/Codeception/Lib/Actor/Shared/Comment.php | 2 +- .../Shared/PhpSuperGlobalsConverter.php | 2 +- src/Codeception/Lib/Console/Output.php | 2 +- src/Codeception/Lib/Console/ReplHistory.php | 2 +- src/Codeception/Lib/Di.php | 5 +- src/Codeception/Lib/Friend.php | 2 +- src/Codeception/Lib/Generator/Cest.php | 2 +- src/Codeception/Lib/Generator/Test.php | 4 +- src/Codeception/Lib/GroupManager.php | 8 +-- src/Codeception/Lib/ModuleContainer.php | 14 ++-- src/Codeception/Module.php | 13 ++-- src/Codeception/Reporter/HtmlReporter.php | 37 +++++----- src/Codeception/Reporter/JUnitReporter.php | 8 +-- src/Codeception/Snapshot.php | 4 +- src/Codeception/Step.php | 14 ++-- src/Codeception/Step/Executor.php | 6 +- src/Codeception/Subscriber/Console.php | 27 ++++---- src/Codeception/Subscriber/Deprecation.php | 2 +- src/Codeception/Subscriber/ErrorHandler.php | 9 ++- src/Codeception/Subscriber/PrepareTest.php | 5 -- src/Codeception/Suite.php | 62 ++++++++--------- src/Codeception/SuiteManager.php | 17 ++--- src/Codeception/Template/Bootstrap.php | 2 +- src/Codeception/Template/Dependencies.php | 2 +- src/Codeception/Test/DataProvider.php | 6 +- .../Test/Feature/ScenarioLoader.php | 2 +- src/Codeception/Test/Filter.php | 14 ++-- src/Codeception/Test/Gherkin.php | 17 ++--- src/Codeception/Test/Loader.php | 4 +- src/Codeception/Test/Loader/Gherkin.php | 4 +- src/Codeception/Test/Loader/Unit.php | 4 +- src/Codeception/Test/Metadata.php | 10 ++- src/Codeception/Test/Test.php | 22 +++--- src/Codeception/Test/TestCaseWrapper.php | 27 +++----- src/Codeception/Test/Unit.php | 17 +++-- src/Codeception/Util/ActionSequence.php | 6 +- src/Codeception/Util/Annotation.php | 25 ++++--- src/Codeception/Util/Autoload.php | 4 +- src/Codeception/Util/Debug.php | 5 +- src/Codeception/Util/FileSystem.php | 6 +- src/Codeception/Util/PathResolver.php | 2 +- src/Codeception/Util/ReflectionHelper.php | 7 +- src/Codeception/Util/StackTraceFilter.php | 16 ++--- src/Codeception/Util/Template.php | 11 ++- tests/cli/BuildCest.php | 49 ++++++------- tests/cli/DryRunCest.php | 6 +- tests/cli/IncludedCest.php | 6 +- tests/cli/RunCest.php | 11 +-- tests/cli/RunSkippedCest.php | 16 ++--- tests/cli/RunUselessTestsCest.php | 4 +- tests/cli/WantToCest.php | 14 ++-- tests/unit/Codeception/ApplicationTest.php | 2 +- .../Codeception/Command/BaseCommandRunner.php | 2 +- tests/unit/Codeception/Command/BuildTest.php | 3 - .../Codeception/Command/GenerateGroupTest.php | 3 - .../Command/GenerateScenarioTest.php | 9 --- .../Command/GenerateStepObjectTest.php | 3 - .../Codeception/Command/GenerateSuiteTest.php | 3 - .../Codeception/Command/GenerateTestTest.php | 2 +- .../unit/Codeception/Coverage/FilterTest.php | 38 ++++------- .../Lib/Console/DiffFactoryTest.php | 3 - tests/unit/Codeception/Lib/DiTest.php | 11 ++- .../Codeception/Lib/ModuleContainerTest.php | 56 +++++++-------- tests/unit/Codeception/Lib/ParserTest.php | 4 +- tests/unit/Codeception/ScenarioTest.php | 4 +- tests/unit/Codeception/Step/RetryTest.php | 3 - tests/unit/Codeception/Step/TryToTest.php | 5 +- tests/unit/Codeception/StepTest.php | 4 +- .../Codeception/Test/DataProviderTest.php | 68 +++++++++---------- tests/unit/Codeception/Util/MockAutoload.php | 3 - .../Codeception/Util/ReflectionHelperTest.php | 22 +++--- 92 files changed, 421 insertions(+), 566 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 606986b374..fc3d3b7a81 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.2' ini-values: memory_limit=-1, date.timezone='UTC' tools: phpcs @@ -45,13 +45,13 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - php: ['8.0', '8.1', '8.2'] + php: ['8.1', '8.2', '8.3'] mode: ['stable', 'experimental'] exclude: - - php: '8.1' - mode: 'experimental' - php: '8.2' mode: 'experimental' + - php: '8.3' + mode: 'experimental' steps: - name: Checkout @@ -87,15 +87,15 @@ jobs: if: matrix.mode == 'stable' run: composer update --no-interaction --no-progress --optimize-autoloader --ansi - - name: Composer install lowest versions of dependencies on PHP 8.0 in experimental mode - if: matrix.php == '8.0' && matrix.mode == 'experimental' + - name: Composer install lowest versions of dependencies on PHP 8.1 in experimental mode + if: matrix.php == '8.1' && matrix.mode == 'experimental' run: composer update --prefer-lowest --no-interaction --no-progress --optimize-autoloader --ansi - name: Test that failing test really fails run: if php codecept run -c tests/data/claypit/ scenario FailedCept -vvv; then echo "Test hasn't failed"; false; fi; -# - name: Run tests without code coverage on PHP 8.0 -# if: matrix.php == '8.0' +# - name: Run tests without code coverage on PHP 8.1 +# if: matrix.php == '8.1' # run: | # php -S 127.0.0.1:8008 -t tests/data/app >/dev/null 2>&1 & # php codecept build @@ -137,7 +137,7 @@ jobs: fail-fast: false matrix: os: [windows-latest] - php: ['8.0', '8.1', '8.2'] + php: ['8.1', '8.2', '8.3'] steps: - name: Checkout @@ -172,7 +172,7 @@ jobs: run: composer install --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - name: Run tests cli - if: matrix.php != '8.1' + if: matrix.php != '8.2' run: php codecept run cli --skip-group coverage - name: Run tests unit diff --git a/composer.json b/composer.json index 31b219bf16..a7783ae478 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "minimum-stability": "RC", "require": { - "php": "^8.0", + "php": "^8.1", "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", diff --git a/ext/Recorder.php b/ext/Recorder.php index d8641dd967..195a7e5bfa 100644 --- a/ext/Recorder.php +++ b/ext/Recorder.php @@ -10,6 +10,7 @@ use Codeception\Exception\ExtensionException; use Codeception\Extension; use Codeception\Lib\Interfaces\ScreenshotSaver; +use Codeception\Module; use Codeception\Module\WebDriver; use Codeception\Step; use Codeception\Step\Comment as CommentStep; @@ -283,7 +284,7 @@ class Recorder extends Extension Events::STEP_AFTER => 'afterStep', ]; - protected ?\Codeception\Module $webDriverModule = null; + protected ?Module $webDriverModule = null; protected ?string $dir = null; @@ -345,12 +346,12 @@ public function beforeSuite(): void public function afterSuite(): void { - if (!$this->webDriverModule) { + if (!$this->webDriverModule instanceof Module) { return; } $links = ''; - if (!empty($this->slides)) { + if ($this->slides !== []) { foreach ($this->recordedTests as $suiteName => $suite) { $links .= "
  • {$suiteName}
    • "; foreach ($suite as $fileName => $tests) { @@ -391,7 +392,7 @@ public function afterSuite(): void public function before(TestEvent $event): void { - if (!$this->webDriverModule) { + if (!$this->webDriverModule instanceof Module) { return; } $this->dir = null; @@ -435,7 +436,7 @@ public function cleanup(TestEvent $event): void } } - if (!$this->webDriverModule || !$this->dir) { + if (!$this->webDriverModule instanceof Module || !$this->dir) { return; } if (!$this->config['delete_successful']) { @@ -450,7 +451,7 @@ public function cleanup(TestEvent $event): void public function persist(TestEvent $event): void { - if (!$this->webDriverModule) { + if (!$this->webDriverModule instanceof Module) { return; } $indicatorHtml = ''; @@ -466,7 +467,7 @@ public function persist(TestEvent $event): void try { !is_dir($dir) && !mkdir($dir) && !is_dir($dir); $this->dir = $dir; - } catch (Exception $exception) { + } catch (Exception) { $this->skipRecording[] = $testPath; $this->appendErrorMessage( $testPath, @@ -486,7 +487,7 @@ public function persist(TestEvent $event): void } $this->webDriverModule->webDriver->takeScreenshot($this->dir . DIRECTORY_SEPARATOR . $filename); - } catch (Exception $exception) { + } catch (Exception) { $this->appendErrorMessage( $testPath, "⏺ Unable to capture a screenshot for {$testPath}/before" @@ -503,13 +504,13 @@ public function persist(TestEvent $event): void $indicatorHtml .= (new Template($this->indicatorTemplate)) ->place('step', (int)$i) - ->place('isActive', (int)$i ? '' : 'active') + ->place('isActive', (int)$i !== 0 ? '' : 'active') ->produce(); $slideHtml .= (new Template($this->slidesTemplate)) ->place('image', $i) ->place('caption', $step->getHtml('#3498db')) - ->place('isActive', (int)$i ? '' : 'active') + ->place('isActive', (int)$i !== 0 ? '' : 'active') ->place('isError', $status === 'success' ? '' : 'error') ->place('timeStamp', $this->timeStamps[$i]) ->produce(); @@ -518,7 +519,7 @@ public function persist(TestEvent $event): void $html = (new Template($this->template)) ->place('indicators', $indicatorHtml) ->place('slides', $slideHtml) - ->place('feature', ucfirst($event->getTest()->getFeature())) + ->place('feature', ucfirst((string) $event->getTest()->getFeature())) ->place('test', Descriptor::getTestSignature($event->getTest())) ->place('carousel_class', $this->config['animate_slides'] ? ' slide' : '') ->produce(); @@ -550,7 +551,7 @@ public function persist(TestEvent $event): void public function afterStep(StepEvent $event): void { - if ($this->webDriverModule === null || $this->dir === null) { + if (!$this->webDriverModule instanceof Module || $this->dir === null) { return; } @@ -571,7 +572,7 @@ public function afterStep(StepEvent $event): void } $this->webDriverModule->webDriver->takeScreenshot($this->dir . DIRECTORY_SEPARATOR . $filename); - } catch (Exception $exception) { + } catch (Exception) { $testPath = codecept_relative_path(Descriptor::getTestFullName($event->getTest())); $this->appendErrorMessage( $testPath, @@ -589,6 +590,7 @@ protected function isStepIgnored(StepEvent $event): bool $configIgnoredSteps = $this->config['ignore_steps']; $annotationIgnoredSteps = $event->getTest()->getMetadata()->getParam('skipRecording'); + /** @var string[] $ignoredSteps */ $ignoredSteps = array_unique( array_merge( $configIgnoredSteps, diff --git a/ext/RunBefore.php b/ext/RunBefore.php index cd18b8da20..3ad4bc954e 100644 --- a/ext/RunBefore.php +++ b/ext/RunBefore.php @@ -111,7 +111,7 @@ private function removeProcessFromMonitoring(int $index): void private function processMonitoring(): void { - while (count($this->processes) !== 0) { + while ($this->processes !== []) { $this->checkProcesses(); sleep(1); } diff --git a/ext/RunFailed.php b/ext/RunFailed.php index e244edcaaf..5380edb04e 100644 --- a/ext/RunFailed.php +++ b/ext/RunFailed.php @@ -88,7 +88,7 @@ public function saveFailed(PrintResultEvent $event): void protected function localizePath(string $path): string { $root = realpath($this->getRootDir()) . DIRECTORY_SEPARATOR; - if (substr($path, 0, strlen($root)) === $root) { + if (str_starts_with($path, $root)) { return substr($path, strlen($root)); } return $path; diff --git a/ext/RunProcess.php b/ext/RunProcess.php index 6389e4dd40..7883e7b7d6 100644 --- a/ext/RunProcess.php +++ b/ext/RunProcess.php @@ -4,6 +4,7 @@ namespace Codeception\Extension; +use BadMethodCallException; use Codeception\Events; use Codeception\Exception\ExtensionException; use Codeception\Extension; @@ -127,7 +128,7 @@ public function stopProcess(): void */ public function __sleep() { - throw new \BadMethodCallException('Cannot serialize ' . __CLASS__); + throw new BadMethodCallException('Cannot serialize ' . self::class); } /** @@ -138,6 +139,6 @@ public function __sleep() */ public function __wakeup() { - throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__); + throw new BadMethodCallException('Cannot unserialize ' . self::class); } } diff --git a/src/Codeception/Actor.php b/src/Codeception/Actor.php index 0e29025065..e01f0ae62a 100644 --- a/src/Codeception/Actor.php +++ b/src/Codeception/Actor.php @@ -42,7 +42,7 @@ public function wantToTest(string $text): void public function __call(string $method, array $arguments) { - $class = $this::class; + $class = static::class; throw new RuntimeException("Call to undefined method {$class}::{$method}"); } diff --git a/src/Codeception/Application.php b/src/Codeception/Application.php index 44cab365f6..c275921669 100644 --- a/src/Codeception/Application.php +++ b/src/Codeception/Application.php @@ -101,7 +101,7 @@ protected function getCustomCommandName(string $commandClass): string */ public function run(InputInterface $input = null, OutputInterface $output = null): int { - if ($input === null) { + if (!$input instanceof InputInterface) { $input = $this->getCoreArguments(); } @@ -135,7 +135,7 @@ protected function getDefaultInputDefinition(): InputDefinition */ protected function getCoreArguments(): SymfonyArgvInput { - if ($this->coreArguments !== null) { + if ($this->coreArguments instanceof SymfonyArgvInput) { return $this->coreArguments; } diff --git a/src/Codeception/Codecept.php b/src/Codeception/Codecept.php index 1018716693..ad8c52916d 100644 --- a/src/Codeception/Codecept.php +++ b/src/Codeception/Codecept.php @@ -78,7 +78,7 @@ class Codecept protected array $extensions = []; - private Output $output; + private readonly Output $output; public function __construct(array $options = []) { @@ -154,7 +154,7 @@ public function registerSubscribers(): void private function isConsolePrinterSubscribed(): bool { - foreach ($this->dispatcher->getListeners() as $event => $listeners) { + foreach ($this->dispatcher->getListeners() as $listeners) { foreach ($listeners as $listener) { if ($listener instanceof ConsolePrinter) { return true; @@ -192,14 +192,6 @@ private function registerReporters(): void } } - private function absolutePath(string $path): string - { - if ((str_starts_with($path, '/')) or (strpos($path, ':') === 1)) { // absolute path - return $path; - } - return Configuration::outputDir() . $path; - } - public function run(string $suite, string $test = null, array $config = null): void { ini_set( @@ -219,7 +211,7 @@ public function run(string $suite, string $test = null, array $config = null): v // Iterate over all unique environment sets and runs the given suite with each of the merged configurations. foreach (array_unique($selectedEnvironments) as $envList) { - $envSet = explode(',', $envList); + $envSet = explode(',', (string) $envList); $suiteEnvConfig = $config; // contains a list of the environments used in this suite configuration env set. @@ -254,9 +246,9 @@ public function runSuite(array $settings, string $suite, string $test = null): v $settings['shard'] = $this->options['shard']; $suiteManager = new SuiteManager($this->dispatcher, $suite, $settings, $this->options); $suiteManager->initialize(); - srand($this->options['seed']); + mt_srand($this->options['seed']); $suiteManager->loadTests($test); - srand(); + mt_srand(); $suiteManager->run($this->resultAggregator); } diff --git a/src/Codeception/Command/Build.php b/src/Codeception/Command/Build.php index c4fb231d39..6e49f19ce3 100644 --- a/src/Codeception/Command/Build.php +++ b/src/Codeception/Command/Build.php @@ -77,7 +77,7 @@ private function buildActions(array $settings): bool private function buildSuiteActors(): void { $suites = $this->getSuites(); - if (!empty($suites)) { + if ($suites !== []) { $this->output->writeln("Building Actor classes for suites: " . implode(', ', $suites) . ''); } foreach ($suites as $suite) { @@ -94,7 +94,7 @@ private function buildSuiteActors(): void } } - protected function buildActorsForConfig($configFile = null): void + protected function buildActorsForConfig(?string $configFile = null): void { $config = $this->getGlobalConfig($configFile); diff --git a/src/Codeception/Command/DryRun.php b/src/Codeception/Command/DryRun.php index a1c448c36c..b49c55070c 100644 --- a/src/Codeception/Command/DryRun.php +++ b/src/Codeception/Command/DryRun.php @@ -33,7 +33,7 @@ use function str_replace; /** - * Shows step by step execution process for scenario driven tests without actually running them. + * Shows step-by-step execution process for scenario driven tests without actually running them. * * * `codecept dry-run acceptance` * * `codecept dry-run acceptance MyCest` @@ -65,7 +65,7 @@ public function getDescription(): string public function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); - $suite = $input->getArgument('suite'); + $suite = (string)$input->getArgument('suite'); $test = $input->getArgument('test'); $config = $this->getGlobalConfig(); @@ -219,7 +219,7 @@ private function returnDefaultValueForIntersectionType(ReflectionIntersectionTyp if ($extends !== null) { $code .= " extends \\$extends"; } - if (count($implements) > 0) { + if ($implements !== []) { $code .= ' implements ' . implode(', ', $implements); } $code .= ' {}'; diff --git a/src/Codeception/Command/GenerateFeature.php b/src/Codeception/Command/GenerateFeature.php index 8fd6f83b7d..b0e1c5feed 100644 --- a/src/Codeception/Command/GenerateFeature.php +++ b/src/Codeception/Command/GenerateFeature.php @@ -45,7 +45,7 @@ public function getDescription(): string public function execute(InputInterface $input, OutputInterface $output): int { $suite = $input->getArgument('suite'); - $filename = $input->getArgument('feature'); + $filename = (string)$input->getArgument('feature'); $config = $this->getSuiteConfig($suite); $this->createDirectoryFor($config['path'], $filename); @@ -54,7 +54,7 @@ public function execute(InputInterface $input, OutputInterface $output): int if (!preg_match('#\.feature$#', $filename)) { $filename .= '.feature'; } - $fullPath = rtrim($config['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename; + $fullPath = rtrim((string) $config['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename; $res = $this->createFile($fullPath, $feature->produce()); if (!$res) { $output->writeln("Feature {$filename} already exists"); diff --git a/src/Codeception/Command/GenerateScenarios.php b/src/Codeception/Command/GenerateScenarios.php index d31767dbc5..27249e1f45 100644 --- a/src/Codeception/Command/GenerateScenarios.php +++ b/src/Codeception/Command/GenerateScenarios.php @@ -57,9 +57,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $suiteConf = $this->getSuiteConfig($suite); - $path = $input->getOption('path') - ? $input->getOption('path') - : Configuration::dataDir() . 'scenarios'; + $path = $input->getOption('path') ?: Configuration::dataDir() . 'scenarios'; $format = $input->getOption('format'); diff --git a/src/Codeception/Command/GenerateStepObject.php b/src/Codeception/Command/GenerateStepObject.php index 848e411db5..ef1b1bdc29 100644 --- a/src/Codeception/Command/GenerateStepObject.php +++ b/src/Codeception/Command/GenerateStepObject.php @@ -52,15 +52,10 @@ public function execute(InputInterface $input, OutputInterface $output): int $path = $this->createDirectoryFor(Configuration::supportDir() . 'Step' . DIRECTORY_SEPARATOR . ucfirst($suite), $step); - /** - * @var QuestionHelper - */ + /** @var QuestionHelper $dialog */ $dialog = $this->getHelperSet()->get('question'); $filename = $path . $class . '.php'; - $helper = $this->getHelper('question'); - $question = new Question("Add action to StepObject class (ENTER to exit): "); - $stepObject = new StepObjectGenerator($config, ucfirst($suite) . '\\' . $step); if (!$input->getOption('silent')) { diff --git a/src/Codeception/Command/GherkinSnippets.php b/src/Codeception/Command/GherkinSnippets.php index a90112f6c1..cba74dc576 100644 --- a/src/Codeception/Command/GherkinSnippets.php +++ b/src/Codeception/Command/GherkinSnippets.php @@ -56,7 +56,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $generator = new GherkinSnippetsGenerator($config, $test); $snippets = $generator->getSnippets(); - if (empty($snippets)) { + if ($snippets === []) { $output->writeln(" All Gherkin steps are defined. Exiting... "); return 0; } diff --git a/src/Codeception/Command/Run.php b/src/Codeception/Command/Run.php index aa815f4f21..e87ffd0e68 100644 --- a/src/Codeception/Command/Run.php +++ b/src/Codeception/Command/Run.php @@ -335,7 +335,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $userOptions['fail-fast'] = (int)$this->options['fail-fast'] ?: 1; } - $suite = $input->getArgument('suite'); + $suite = (string)$input->getArgument('suite'); $test = $input->getArgument('test'); if ($this->options['group']) { @@ -513,7 +513,7 @@ public function execute(InputInterface $input, OutputInterface $output): int return 0; } - protected function matchSingleTest($suite, $config): ?array + protected function matchSingleTest(string $suite, array $config): ?array { // Workaround when codeception.yml is inside tests directory and tests path is set to "." // @see https://github.com/Codeception/Codeception/issues/4432 @@ -589,13 +589,13 @@ protected function runIncludedSuites( $config = Configuration::config($currentDir); if (!empty($defaultConfig['groups'])) { - $groups = array_map(fn ($g) => $absolutePath . $g, $defaultConfig['groups']); + $groups = array_map(fn ($g): string => $absolutePath . $g, $defaultConfig['groups']); Configuration::append(['groups' => $groups]); } $suites = Configuration::suites(); - if (!empty($filterSuitesByWildcard)) { + if ($filterSuitesByWildcard !== []) { $suites = array_intersect($suites, $filterSuitesByWildcard); } @@ -701,7 +701,7 @@ private function matchFilteredTestName(string &$path): ?string // phpunit --filter matches against the fully qualified method name, so tests actually begin with : $caratPos = strpos($filter, '^'); if ($caratPos !== false) { - $filter = substr_replace($filter, ':', $caratPos, 1); + return substr_replace($filter, ':', $caratPos, 1); } return $filter; } @@ -789,7 +789,7 @@ private function addRuntimeOptionsToCurrentConfig(array $config): array } // enable extensions if ($this->options['ext']) { - $config = $this->enableExtensions($this->options['ext']); + return $this->enableExtensions($this->options['ext']); } return $config; diff --git a/src/Codeception/Command/Shared/ActorTrait.php b/src/Codeception/Command/Shared/ActorTrait.php index e46a7fdaf9..f6f25b604a 100644 --- a/src/Codeception/Command/Shared/ActorTrait.php +++ b/src/Codeception/Command/Shared/ActorTrait.php @@ -29,7 +29,7 @@ protected function getActorClassName(): ?string return $namespace . $this->settings['actor']; } - private function getActor($test) + private function getActor($test): ?object { $actorClass = $this->getActorClassName(); diff --git a/src/Codeception/Configuration.php b/src/Codeception/Configuration.php index d123d62cfb..15c99b3f3e 100644 --- a/src/Codeception/Configuration.php +++ b/src/Codeception/Configuration.php @@ -212,7 +212,7 @@ public static function config(string $configFile = null): array $config = self::mergeConfigs($config, self::getConfFromContents($configContents, $configFile)); } - if ($config == self::$defaultConfig) { + if ($config === self::$defaultConfig) { throw new ConfigurationException("Configuration file is invalid"); } @@ -361,7 +361,7 @@ public static function suiteSettings(string $suite, array $config): array $settings['path'] = $suite; } - $config['paths']['tests'] = str_replace('/', DIRECTORY_SEPARATOR, $config['paths']['tests']); + $config['paths']['tests'] = str_replace('/', DIRECTORY_SEPARATOR, (string) $config['paths']['tests']); $settings['path'] = self::$dir . DIRECTORY_SEPARATOR . $config['paths']['tests'] . DIRECTORY_SEPARATOR . $settings['path'] . DIRECTORY_SEPARATOR; @@ -495,7 +495,7 @@ public static function modules(array $settings): array { return array_filter( array_map( - fn ($m) => is_array($m) ? key($m) : $m, + fn ($m): mixed => is_array($m) ? key($m) : $m, $settings['modules']['enabled'], array_keys($settings['modules']['enabled']) ), @@ -723,7 +723,7 @@ protected static function loadSuiteConfig(string $suite, string $path, array $se */ protected static function expandWildcardedIncludes(array $includes): array { - if (empty($includes)) { + if ($includes === []) { return $includes; } $expandedIncludes = []; diff --git a/src/Codeception/Coverage/Filter.php b/src/Codeception/Coverage/Filter.php index f572936ad0..98c2dd37b9 100644 --- a/src/Codeception/Coverage/Filter.php +++ b/src/Codeception/Coverage/Filter.php @@ -66,9 +66,9 @@ public function whiteList(array $config): self throw new ConfigurationException('Error parsing yaml. Config `whitelist: include:` should be an array'); } foreach ($coverage['whitelist']['include'] as $fileOrDir) { - $finder = !str_contains($fileOrDir, '*') - ? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir] - : $this->matchWildcardPattern($fileOrDir); + $finder = str_contains((string) $fileOrDir, '*') + ? $this->matchWildcardPattern($fileOrDir) + : [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]; foreach ($finder as $file) { $filter->includeFile((string)$file); @@ -83,9 +83,9 @@ public function whiteList(array $config): self foreach ($coverage['whitelist']['exclude'] as $fileOrDir) { try { - $finder = !str_contains($fileOrDir, '*') - ? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir] - : $this->matchWildcardPattern($fileOrDir); + $finder = str_contains((string) $fileOrDir, '*') + ? $this->matchWildcardPattern($fileOrDir) + : [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]; foreach ($finder as $file) { $filter->excludeFile((string)$file); @@ -110,11 +110,11 @@ private function newWhiteList(array $whitelist): self throw new ConfigurationException('Error parsing yaml. Config `whitelist: exclude:` should be an array'); } - if (count($exclude) === 0 && count($include) === 0) { + if ($exclude === [] && $include === []) { return $this; } - if (count($include) === 0) { + if ($include === []) { $include = [ Configuration::projectDir() . DIRECTORY_SEPARATOR . '*' ]; @@ -122,9 +122,9 @@ private function newWhiteList(array $whitelist): self $allIncludedFiles = []; foreach ($include as $fileOrDir) { - $finder = !str_contains($fileOrDir, '*') - ? $this->matchFileOrDirectory($fileOrDir) - : $this->matchWildcardPattern($fileOrDir); + $finder = str_contains((string) $fileOrDir, '*') + ? $this->matchWildcardPattern($fileOrDir) + : $this->matchFileOrDirectory($fileOrDir); $allIncludedFiles += iterator_to_array($finder->getIterator()); } @@ -132,9 +132,9 @@ private function newWhiteList(array $whitelist): self $allExcludedFiles = []; foreach ($exclude as $fileOrDir) { try { - $finder = !str_contains($fileOrDir, '*') - ? $this->matchFileOrDirectory($fileOrDir) - : $this->matchWildcardPattern($fileOrDir); + $finder = str_contains((string) $fileOrDir, '*') + ? $this->matchWildcardPattern($fileOrDir) + : $this->matchFileOrDirectory($fileOrDir); $allExcludedFiles += iterator_to_array($finder->getIterator()); } catch (DirectoryNotFoundException) { diff --git a/src/Codeception/Coverage/PhpCodeCoverageFactory.php b/src/Codeception/Coverage/PhpCodeCoverageFactory.php index 182af7b39d..9465032bf4 100644 --- a/src/Codeception/Coverage/PhpCodeCoverageFactory.php +++ b/src/Codeception/Coverage/PhpCodeCoverageFactory.php @@ -15,7 +15,7 @@ class PhpCodeCoverageFactory public static function build(): CodeCoverage { - if (self::$instance !== null) { + if (self::$instance instanceof CodeCoverage) { return self::$instance; } diff --git a/src/Codeception/Coverage/Subscriber/LocalServer.php b/src/Codeception/Coverage/Subscriber/LocalServer.php index f2c971436a..fa52362505 100644 --- a/src/Codeception/Coverage/Subscriber/LocalServer.php +++ b/src/Codeception/Coverage/Subscriber/LocalServer.php @@ -199,7 +199,7 @@ protected function preProcessCoverage(CodeCoverage $coverage): self return $this; } - $workDir = rtrim($this->settings['work_dir'], '/\\') . DIRECTORY_SEPARATOR; + $workDir = rtrim((string) $this->settings['work_dir'], '/\\') . DIRECTORY_SEPARATOR; $projectDir = Configuration::projectDir(); $coverageData = $coverage->getData(true); //We only want covered files, not all whitelisted ones. @@ -208,7 +208,7 @@ protected function preProcessCoverage(CodeCoverage $coverage): self foreach ($coverageData as $path => $datum) { unset($coverageData[$path]); - $path = str_replace($workDir, $projectDir, $path); + $path = str_replace($workDir, $projectDir, (string) $path); $coverageData[$path] = $datum; } @@ -229,7 +229,7 @@ protected function c3Request(string $action): string|false $http_response_header, fn ($h) => preg_match('#^HTTP(.*?)\s200#', $h) ); - if (empty($okHeaders)) { + if ($okHeaders === []) { throw new RemoteException("Request was not successful. See response header: " . $http_response_header[0]); } if ($contents === false) { @@ -254,7 +254,7 @@ protected function startCoverageCollection($testName): void $cookieDomain = $this->settings['cookie_domain'] ?? null; if (!$cookieDomain) { - $c3Url = parse_url($this->settings['c3_url'] ?: $this->module->_getUrl()); + $c3Url = parse_url((string) ($this->settings['c3_url'] ?: $this->module->_getUrl())); // we need to separate coverage cookies by host; we can't separate cookies by port. $cookieDomain = $c3Url['host'] ?? 'localhost'; @@ -326,6 +326,7 @@ protected function fetchErrors(): void } } + /** @param string[] $headers */ protected function getRemoteError(array $headers): void { foreach ($headers as $header) { @@ -338,7 +339,7 @@ protected function getRemoteError(array $headers): void protected function addC3AccessHeader(string $header, string $value): void { $headerString = "{$header}: {$value}\r\n"; - if (!str_contains($this->c3Access['http']['header'], $headerString)) { + if (!str_contains((string) $this->c3Access['http']['header'], $headerString)) { $this->c3Access['http']['header'] .= $headerString; } } diff --git a/src/Codeception/Lib/Actor/Shared/Comment.php b/src/Codeception/Lib/Actor/Shared/Comment.php index fe9a8a6108..44e20d68ed 100644 --- a/src/Codeception/Lib/Actor/Shared/Comment.php +++ b/src/Codeception/Lib/Actor/Shared/Comment.php @@ -29,7 +29,7 @@ public function am(string $role): self { $role = trim($role); - if (stripos('aeiou', (string)$role[0]) !== false) { + if (stripos('aeiou', $role[0]) !== false) { return $this->comment('As an ' . $role); } diff --git a/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php b/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php index 41d57c5cbb..7932b9e5db 100644 --- a/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php +++ b/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php @@ -73,7 +73,7 @@ private function rearrangeFiles(array $requestFiles): array * to ['tmp_name' => ['a' => '/tmp/test.txt'] ] */ $innerInfo = array_map( - fn ($v) => [$innerName => $v], + fn ($v): array => [$innerName => $v], $innerInfo ); diff --git a/src/Codeception/Lib/Console/Output.php b/src/Codeception/Lib/Console/Output.php index 3740bc434a..4e4160deef 100644 --- a/src/Codeception/Lib/Console/Output.php +++ b/src/Codeception/Lib/Console/Output.php @@ -34,7 +34,7 @@ public function __construct(array $config) // enable interactive output mode for CLI $this->isInteractive = $this->config['interactive'] && isset($_SERVER['TERM']) - && PHP_SAPI == 'cli' + && PHP_SAPI === 'cli' && $_SERVER['TERM'] != 'linux'; $formatter = new OutputFormatter($this->config['colors']); diff --git a/src/Codeception/Lib/Console/ReplHistory.php b/src/Codeception/Lib/Console/ReplHistory.php index cdb9f0e975..a6ecc34d06 100644 --- a/src/Codeception/Lib/Console/ReplHistory.php +++ b/src/Codeception/Lib/Console/ReplHistory.php @@ -47,7 +47,7 @@ public function clear(): void public function save(): void { - if (empty($this->stashedCommands)) { + if ($this->stashedCommands === []) { return; } diff --git a/src/Codeception/Lib/Di.php b/src/Codeception/Lib/Di.php index 5a6f6e370d..35b0a60809 100644 --- a/src/Codeception/Lib/Di.php +++ b/src/Codeception/Lib/Di.php @@ -11,6 +11,7 @@ use ReflectionException; use ReflectionMethod; use ReflectionObject; +use Throwable; class Di { @@ -66,7 +67,7 @@ public function instantiate( } // get class from parent container - if ($this->fallback && ($class = $this->fallback->get($className))) { + if ($this->fallback instanceof Di && ($class = $this->fallback->get($className))) { return $class; } @@ -116,7 +117,7 @@ public function injectDependencies(object $object, string $injectMethodName = se $args = $this->prepareArgs($reflectedMethod, $defaults); } catch (Exception $e) { $msg = $e->getMessage(); - if ($e->getPrevious() !== null) { // injection failed because PHP code is invalid. See #3869 + if ($e->getPrevious() instanceof Throwable) { // injection failed because PHP code is invalid. See #3869 $msg .= '; ' . $e->getPrevious(); } throw new InjectionException( diff --git a/src/Codeception/Lib/Friend.php b/src/Codeception/Lib/Friend.php index 99ee0a17c8..7c583c9c38 100644 --- a/src/Codeception/Lib/Friend.php +++ b/src/Codeception/Lib/Friend.php @@ -18,7 +18,7 @@ public function __construct(protected string $name, protected Actor $actor, arra { $this->multiSessionModules = array_filter($modules, fn ($m): bool => $m instanceof MultiSession); - if (empty($this->multiSessionModules)) { + if ($this->multiSessionModules === []) { throw new TestRuntimeException("No multisession modules used. Can't instantiate friend"); } } diff --git a/src/Codeception/Lib/Generator/Cest.php b/src/Codeception/Lib/Generator/Cest.php index f27924fc8f..d3e9baed49 100644 --- a/src/Codeception/Lib/Generator/Cest.php +++ b/src/Codeception/Lib/Generator/Cest.php @@ -47,7 +47,7 @@ public function produce(): string throw new ConfigurationException("Cest can't be created for suite without an actor. Add `actor: SomeTester` to suite config"); } - $namespaceHeader = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst($this->settings['suite']) . '\\' . $this->name); + $namespaceHeader = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst((string) $this->settings['suite']) . '\\' . $this->name); if ($namespaceHeader) { $namespaceHeader .= "\nuse " . $this->supportNamespace() . $actor . ";"; diff --git a/src/Codeception/Lib/Generator/Test.php b/src/Codeception/Lib/Generator/Test.php index 7298a952b1..74ee0ebf88 100644 --- a/src/Codeception/Lib/Generator/Test.php +++ b/src/Codeception/Lib/Generator/Test.php @@ -52,7 +52,7 @@ public function produce(): string { $actor = $this->settings['actor']; - $ns = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst($this->settings['suite']) . '\\' . $this->name); + $ns = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst((string) $this->settings['suite']) . '\\' . $this->name); if ($ns) { $ns .= "\nuse " . $this->supportNamespace() . $actor . ";"; @@ -63,7 +63,7 @@ public function produce(): string if ($this->settings['actor']) { $tester = (new Template($this->testerTemplate)) ->place('actorClass', $actor) - ->place('actor', lcfirst(Configuration::config()['actor_suffix'])) + ->place('actor', lcfirst((string) Configuration::config()['actor_suffix'])) ->produce(); } diff --git a/src/Codeception/Lib/GroupManager.php b/src/Codeception/Lib/GroupManager.php index 91464a911c..a193b3a897 100644 --- a/src/Codeception/Lib/GroupManager.php +++ b/src/Codeception/Lib/GroupManager.php @@ -10,7 +10,6 @@ use Codeception\Test\Test; use Codeception\Util\PathResolver; use Symfony\Component\Finder\Finder; -use Symfony\Component\Finder\SplFileInfo; use function realpath; @@ -23,6 +22,7 @@ class GroupManager protected string $rootDir; + /** @param string[] $configuredGroups */ public function __construct(protected array $configuredGroups) { $this->rootDir = Configuration::baseDir(); @@ -58,7 +58,6 @@ protected function loadGroupsByPattern(): void ->in($path); foreach ($files as $file) { - /** @var SplFileInfo $file * */ $prefix = str_replace('*', '', $group); $pathPrefix = str_replace('*', '', basename($pattern)); $groupName = $prefix . str_replace($pathPrefix, '', $file->getRelativePathname()); @@ -76,7 +75,7 @@ protected function loadConfiguredGroupSettings(): void $this->testsInGroups[$group] = []; if (is_array($tests)) { foreach ($tests as $test) { - $file = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $test); + $file = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], (string) $test); $this->testsInGroups[$group][] = $this->normalizeFilePath($file, $group); } continue; @@ -149,12 +148,13 @@ public function groupsForTest(Test $test): array $groups = $test->getMetadata()->getGroups(); foreach ($this->testsInGroups as $group => $tests) { + /** @var string[] $tests */ foreach ($tests as $testPattern) { if ($filename == $testPattern) { $groups[] = $group; } - if (str_starts_with($filename . ':' . $testName, (string)$testPattern)) { + if (str_starts_with($filename . ':' . $testName, $testPattern)) { $groups[] = $group; } if ( diff --git a/src/Codeception/Lib/ModuleContainer.php b/src/Codeception/Lib/ModuleContainer.php index bda5655dc0..02cde00a9c 100644 --- a/src/Codeception/Lib/ModuleContainer.php +++ b/src/Codeception/Lib/ModuleContainer.php @@ -75,7 +75,7 @@ class ModuleContainer private array $actions = []; - public function __construct(private Di $di, private array $config) + public function __construct(private readonly Di $di, private array $config) { $this->di->set($this); } @@ -104,7 +104,7 @@ public function create(string $moduleName, bool $active = true): ?object $config = $this->getModuleConfig($moduleName); - if (empty($config) && !$active) { + if ($config === [] && !$active) { // For modules that are a dependency of other modules we want to skip the validation of the config. // This config validation is performed in \Codeception\Module::__construct(). // Explicitly setting $config to null skips this validation. @@ -202,7 +202,7 @@ private function includeMethodAsAction(Module $module, ReflectionMethod $method, // If a part is configured for the module, only include actions from that part if ($configuredParts) { $moduleParts = Annotation::forMethod($module, $method->name)->fetchAll('part'); - if (!array_uintersect($moduleParts, $configuredParts, 'strcasecmp')) { + if (array_uintersect($moduleParts, $configuredParts, 'strcasecmp') === []) { return false; } } @@ -246,7 +246,7 @@ public function hasModule(string $moduleName): bool public function getModule(string $moduleName): Module { if (!$this->hasModule($moduleName)) { - $this->throwMissingModuleExceptionWithSuggestion(__CLASS__, $moduleName); + $this->throwMissingModuleExceptionWithSuggestion(self::class, $moduleName); } return $this->modules[$moduleName]; @@ -343,7 +343,7 @@ private function injectModuleDependencies(string $moduleName, DependsOnModule $m * * @throws ModuleException|ModuleRequireException */ - private function checkForMissingDependencies(string $moduleName, $module): void + private function checkForMissingDependencies(string $moduleName, DependsOnModule $module): void { $dependencies = $this->getModuleDependencies($module); $configuredDependenciesCount = count($this->getConfiguredDependencies($moduleName)); @@ -370,7 +370,7 @@ private function getModuleDependencies(DependsOnModule $module): array { $depends = $module->_depends(); - if (!$depends) { + if ($depends === []) { return []; } @@ -493,7 +493,7 @@ private function validateConflict(Module $module, Module $otherModule): void * * @return class-string|Module|string */ - private function normalizeConflictSpecification(string $conflicts) + private function normalizeConflictSpecification(string $conflicts): string|Module { if (interface_exists($conflicts) || class_exists($conflicts)) { return $conflicts; diff --git a/src/Codeception/Module.php b/src/Codeception/Module.php index ebf1db81ab..6702ce722d 100644 --- a/src/Codeception/Module.php +++ b/src/Codeception/Module.php @@ -134,7 +134,7 @@ protected function validateConfig(): void $fields = array_keys($this->config); if (array_intersect($this->requiredFields, $fields) !== $this->requiredFields) { throw new ModuleConfigException( - $this::class, + static::class, "\nOptions: " . implode(', ', $this->requiredFields) . " are required\n" . "Please, update the configuration and set all the required fields\n\n" ); @@ -158,7 +158,7 @@ protected function validateConfig(): void */ public function _getName(): string { - $moduleName = '\\' . $this::class; + $moduleName = '\\' . static::class; if (str_starts_with($moduleName, ModuleContainer::MODULE_NAMESPACE)) { return substr($moduleName, strlen(ModuleContainer::MODULE_NAMESPACE)); @@ -172,7 +172,7 @@ public function _getName(): string */ public function _hasRequiredFields(): bool { - return !empty($this->requiredFields); + return $this->requiredFields !== []; } /** @@ -287,7 +287,7 @@ protected function getModules(): array protected function getModule(string $name): Module { if (!$this->hasModule($name)) { - $this->moduleContainer->throwMissingModuleExceptionWithSuggestion(__CLASS__, $name); + $this->moduleContainer->throwMissingModuleExceptionWithSuggestion(self::class, $name); } return $this->moduleContainer->getModule($name); } @@ -303,10 +303,7 @@ public function _getConfig(string $key = null): mixed if (!$key) { return $this->config; } - if (isset($this->config[$key])) { - return $this->config[$key]; - } - return null; + return $this->config[$key] ?? null; } protected function scalarizeArray(array $array): array diff --git a/src/Codeception/Reporter/HtmlReporter.php b/src/Codeception/Reporter/HtmlReporter.php index ba00c7227d..7bfb98ad3e 100644 --- a/src/Codeception/Reporter/HtmlReporter.php +++ b/src/Codeception/Reporter/HtmlReporter.php @@ -14,7 +14,6 @@ use Codeception\Test\Descriptor; use Codeception\Test\Interfaces\ScenarioDriven; use Codeception\Test\Test; -use Codeception\TestInterface; use Codeception\Util\PathResolver; use SebastianBergmann\Template\Template; use SebastianBergmann\Timer\Timer; @@ -167,16 +166,14 @@ public function printTestEvent(TestEvent $event, string $scenarioStatus): void $png = ''; $html = ''; - if ($test instanceof TestInterface) { - $reports = $test->getMetadata()->getReports(); - if (isset($reports['png'])) { - $localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir()); - $png = "
      failure screenshot
      "; - } - if (isset($reports['html'])) { - $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir()); - $html = "See HTML snapshot of a failed page"; - } + $reports = $test->getMetadata()->getReports(); + if (isset($reports['png'])) { + $localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir()); + $png = "
      failure screenshot
      "; + } + if (isset($reports['html'])) { + $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir()); + $html = "See HTML snapshot of a failed page"; } $toggle = $stepsBuffer ? '+' : ''; @@ -249,16 +246,14 @@ public function printTestResult(Test $test, float $time, string $scenarioStatus) $png = ''; $html = ''; - if ($test instanceof TestInterface) { - $reports = $test->getMetadata()->getReports(); - if (isset($reports['png'])) { - $localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir()); - $png = "
      failure screenshot
      "; - } - if (isset($reports['html'])) { - $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir()); - $html = "See HTML snapshot of a failed page"; - } + $reports = $test->getMetadata()->getReports(); + if (isset($reports['png'])) { + $localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir()); + $png = "
      failure screenshot
      "; + } + if (isset($reports['html'])) { + $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir()); + $html = "See HTML snapshot of a failed page"; } $toggle = $stepsBuffer ? '+' : ''; diff --git a/src/Codeception/Reporter/JUnitReporter.php b/src/Codeception/Reporter/JUnitReporter.php index 21b8f95473..74112969bc 100644 --- a/src/Codeception/Reporter/JUnitReporter.php +++ b/src/Codeception/Reporter/JUnitReporter.php @@ -218,7 +218,7 @@ public function startTest(TestEvent $event): void $this->currentTestCase = $this->document->createElement('testcase'); foreach ($test->getReportFields() as $attr => $value) { - if ($this->isStrict and !in_array($attr, $this->strictAttributes)) { + if ($this->isStrict && !in_array($attr, $this->strictAttributes)) { continue; } $this->currentTestCase->setAttribute($attr, $value); @@ -313,7 +313,7 @@ public function testFailure(FailEvent $event): void public function testSkipped(FailEvent $event): void { - if ($this->currentTestCase === null) { + if (!$this->currentTestCase instanceof DOMElement) { return; } @@ -325,7 +325,7 @@ public function testSkipped(FailEvent $event): void public function testUseless(FailEvent $event): void { - if ($this->currentTestCase === null) { + if (!$this->currentTestCase instanceof DOMElement) { return; } @@ -343,7 +343,7 @@ public function testUseless(FailEvent $event): void */ private function doAddFault(Test $test, Throwable $t, string $type): void { - if ($this->currentTestCase === null) { + if (!$this->currentTestCase instanceof DOMElement) { return; } diff --git a/src/Codeception/Snapshot.php b/src/Codeception/Snapshot.php index b1eeb640d9..ac21122613 100644 --- a/src/Codeception/Snapshot.php +++ b/src/Codeception/Snapshot.php @@ -78,7 +78,7 @@ protected function save(): void protected function getFileName(): string { if (!$this->fileName) { - $this->fileName = preg_replace('#\W#', '.', $this::class) . '.' . $this->extension; + $this->fileName = preg_replace('#\W#', '.', static::class) . '.' . $this->extension; } return codecept_data_dir() . $this->fileName; } @@ -168,6 +168,6 @@ public function setSnapshotFileExtension(string $fileExtension = 'json'): void private function printDebug(string $message): void { - Debug::debug($this::class . ': ' . $message); + Debug::debug(static::class . ': ' . $message); } } diff --git a/src/Codeception/Step.php b/src/Codeception/Step.php index fa28ceb09d..38643ce662 100644 --- a/src/Codeception/Step.php +++ b/src/Codeception/Step.php @@ -41,6 +41,7 @@ abstract class Step implements Stringable protected bool $isTry = false; + /** @param string[] $arguments */ public function __construct(protected string $action, protected array $arguments = []) { } @@ -64,14 +65,14 @@ public function saveTrace(): void $this->addMetaStep($traceLine, $stack); } - private function isTestFile(string $file) + private function isTestFile(string $file): int|false { return preg_match('#[^\\' . DIRECTORY_SEPARATOR . '](Cest|Cept|Test).php$#', $file); } public function getName(): string { - $class = explode('\\', __CLASS__); + $class = explode('\\', self::class); return end($class); } @@ -124,10 +125,7 @@ public function getArgumentsAsString(int $maxLength = self::DEFAULT_MAX_LENGTH): uasort($arguments, function ($arg1, $arg2): int { $length1 = mb_strlen($arg1, 'utf-8'); $length2 = mb_strlen($arg2, 'utf-8'); - if ($length1 === $length2) { - return 0; - } - return ($length1 < $length2) ? -1 : 1; + return $length1 <=> $length2; }); $allowedLength = floor(($maxLength - $argumentCount + 1) / $argumentCount); @@ -243,7 +241,7 @@ public function toString(int $maxLength): string public function getHtml(string $highlightColor = '#732E81'): string { - if (empty($this->arguments)) { + if ($this->arguments === []) { return sprintf('%s %s', ucfirst($this->prefix), $this->humanize($this->getAction())); } @@ -279,7 +277,7 @@ protected function humanize(string $text): string public function run(ModuleContainer $container = null) { $this->executed = true; - if ($container === null) { + if (!$container instanceof ModuleContainer) { return null; } $activeModule = $container->moduleForAction($this->action); diff --git a/src/Codeception/Step/Executor.php b/src/Codeception/Step/Executor.php index 122e70ff53..73a5624b1d 100644 --- a/src/Codeception/Step/Executor.php +++ b/src/Codeception/Step/Executor.php @@ -10,13 +10,9 @@ class Executor extends CodeceptionStep { - protected Closure $callable; - - public function __construct(Closure $callable, array $arguments = []) + public function __construct(protected Closure $callable, array $arguments = []) { parent::__construct('execute callable function', []); - - $this->callable = $callable; } public function run(ModuleContainer $container = null) diff --git a/src/Codeception/Subscriber/Console.php b/src/Codeception/Subscriber/Console.php index e20494fe7b..254447b2e6 100644 --- a/src/Codeception/Subscriber/Console.php +++ b/src/Codeception/Subscriber/Console.php @@ -31,6 +31,7 @@ use PHPUnit\Framework\IncompleteTestError; use PHPUnit\Framework\SelfDescribing; use PHPUnit\Framework\SkippedTest; +use SebastianBergmann\Comparator\ComparisonFailure; use SebastianBergmann\Timer\Duration; use SebastianBergmann\Timer\ResourceUsageFormatter; use SebastianBergmann\Timer\Timer; @@ -42,7 +43,6 @@ use function array_map; use function array_merge; use function array_reverse; -use function array_shift; use function codecept_relative_path; use function count; use function exec; @@ -172,7 +172,7 @@ public function beforeSuite(SuiteEvent $event): void implode( ', ', array_map( - fn ($module) => $module->_getName(), + fn ($module): string => $module->_getName(), $event->getSuite()->getModules() ) ) @@ -255,7 +255,6 @@ private function printResourceUsage(Duration $duration): void /** * @param FailEvent[] $defects - * @param string $type */ private function printDefects(array $defects, string $type): void { @@ -319,11 +318,11 @@ protected function printFooter(PrintResultEvent $event): void if ($result->wasSuccessful()) { $style = 'warning'; $this->message('OK, but incomplete, skipped, or useless tests!')->style($style)->writeln(); - } elseif ($result->errorCount()) { + } elseif ($result->errorCount() !== 0) { $this->message('ERRORS!')->style($style)->writeln(); - } elseif ($result->failureCount()) { + } elseif ($result->failureCount() !== 0) { $this->message('FAILURES!')->style($style)->writeln(); - } elseif ($result->warningCount()) { + } elseif ($result->warningCount() !== 0) { $style = 'warning'; $this->message('WARNINGS!')->style($style)->writeln(); } @@ -442,7 +441,7 @@ public function beforeStep(StepEvent $event): void } $metaStep = $event->getStep()->getMetaStep(); - if ($metaStep && $this->metaStep != $metaStep) { + if ($metaStep instanceof Meta && $this->metaStep != $metaStep) { $this->message(' ' . $metaStep->getPrefix()) ->style('bold') ->append($metaStep->__toString()) @@ -459,17 +458,17 @@ private function printStep(Step $step): void return; // don't print empty comments } $msg = $this->message(' '); - if ($this->metaStep) { + if ($this->metaStep instanceof Meta) { $msg->append(' '); } $msg->append($step->getPrefix()); $prefixLength = $msg->getLength(); - if (!$this->metaStep) { + if (!$this->metaStep instanceof Meta) { $msg->style('bold'); } $maxLength = $this->width - $prefixLength; $msg->append(OutputFormatter::escape($step->toString($maxLength))); - if ($this->metaStep) { + if ($this->metaStep instanceof Meta) { $msg->style('info'); } $msg->writeln(); @@ -531,7 +530,7 @@ public function printReports(TestInterface $failedTest): void return; } $reports = $failedTest->getMetadata()->getReports(); - if (!empty($reports)) { + if ($reports !== []) { $this->output->writeln('Artifacts:'); $this->output->writeln(''); } @@ -563,7 +562,7 @@ public function printException($exception, string $cause = null): void if ($exception instanceof ExpectationFailedException) { $comparisonFailure = $exception->getComparisonFailure(); - if ($comparisonFailure !== null) { + if ($comparisonFailure instanceof ComparisonFailure) { $message->append($this->messageFactory->prepareComparisonFailureMessage($comparisonFailure)); } } @@ -779,9 +778,7 @@ protected function writelnFinishedTest(TestEvent $event, Message $result): void $numFails = count( array_filter( $test->getScenario()?->getSteps() ?? [], - function (Step $step) { - return $step->hasFailed() && $step instanceof ConditionalAssertion; - } + fn(Step $step): bool => $step->hasFailed() && $step instanceof ConditionalAssertion ) ); diff --git a/src/Codeception/Subscriber/Deprecation.php b/src/Codeception/Subscriber/Deprecation.php index c08c9253e9..4e60ec2c6a 100644 --- a/src/Codeception/Subscriber/Deprecation.php +++ b/src/Codeception/Subscriber/Deprecation.php @@ -35,7 +35,7 @@ public function __construct(array $options) public function afterSuite(SuiteEvent $event): void { $messages = Notification::all(); - if (count($messages) === 0) { + if ($messages === []) { return; } diff --git a/src/Codeception/Subscriber/ErrorHandler.php b/src/Codeception/Subscriber/ErrorHandler.php index 7cad330b4f..127eea0b15 100644 --- a/src/Codeception/Subscriber/ErrorHandler.php +++ b/src/Codeception/Subscriber/ErrorHandler.php @@ -4,12 +4,12 @@ namespace Codeception\Subscriber; +use Codeception\Event\SuiteEvent; +use Codeception\Events; use Codeception\Exception\Deprecation; use Codeception\Exception\Error; use Codeception\Exception\Notice; use Codeception\Exception\Warning; -use Codeception\Event\SuiteEvent; -use Codeception\Events; use Codeception\Lib\Notification; use PHPUnit\Framework\Error\Deprecated as PHPUnit9Deprecation; use PHPUnit\Framework\Error\Error as PHPUnit9Error; @@ -21,7 +21,6 @@ use function call_user_func; use function class_exists; -use function count; use function error_get_last; use function error_reporting; use function getenv; @@ -99,7 +98,7 @@ public function handle(SuiteEvent $event): void // and silence DeprecationErrorHandler yelling about 'THE ERROR HANDLER HAS CHANGED!' register_shutdown_function([$this, 'shutdownHandler']); $this->registerDeprecationErrorHandler(); - $this->oldHandler = set_error_handler([$this, 'errorHandler']); + $this->oldHandler = set_error_handler($this->errorHandler(...)); $this->initialized = true; } @@ -184,7 +183,7 @@ private function registerDeprecationErrorHandler(): void if ( $old && is_array($old) - && count($old) > 0 + && $old !== [] && $old[0] instanceof \Symfony\Component\Debug\ErrorHandler ) { restore_error_handler(); diff --git a/src/Codeception/Subscriber/PrepareTest.php b/src/Codeception/Subscriber/PrepareTest.php index 002fedbc32..1e853526b9 100644 --- a/src/Codeception/Subscriber/PrepareTest.php +++ b/src/Codeception/Subscriber/PrepareTest.php @@ -9,7 +9,6 @@ use Codeception\Lib\Di; use Codeception\Test\Cest; use Codeception\Test\Unit; -use Codeception\TestInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class PrepareTest implements EventSubscriberInterface @@ -29,10 +28,6 @@ public function prepare(TestEvent $event): void { $test = $event->getTest(); - if (!$test instanceof TestInterface) { - return; - } - $prepareMethods = $test->getMetadata()->getParam('prepare'); if (!$prepareMethods) { diff --git a/src/Codeception/Suite.php b/src/Codeception/Suite.php index 5d30c5fa62..e71a80e58a 100644 --- a/src/Codeception/Suite.php +++ b/src/Codeception/Suite.php @@ -15,7 +15,9 @@ use PHPUnit\Framework\SkippedTestError; use PHPUnit\Framework\SkippedWithMessageException; use PHPUnit\Runner\Version; +use PHPUnit\TextUI\CliArguments\Builder; use PHPUnit\TextUI\Configuration\Registry; +use PHPUnit\TextUI\XmlConfiguration\DefaultConfiguration; use Symfony\Component\EventDispatcher\EventDispatcher; use function count; @@ -40,7 +42,7 @@ class Suite */ private array $tests = []; - public function __construct(private EventDispatcher $dispatcher, private string $name = '') + public function __construct(private readonly EventDispatcher $dispatcher, private readonly string $name = '') { } @@ -76,7 +78,7 @@ public function collectCodeCoverage(bool $enabled): void public function run(ResultAggregator $result): void { - if (count($this->tests) === 0) { + if ($this->tests === []) { return; } @@ -88,32 +90,28 @@ public function run(ResultAggregator $result): void } $this->dispatcher->dispatch(new TestEvent($test), Events::TEST_START); - if ($test instanceof TestInterface) { - if ($test->getMetadata()->isBlocked()) { - $result->addTest($test); - - $skip = $test->getMetadata()->getSkip(); - if ($skip !== null) { - if (class_exists(SkippedWithMessageException::class)) { - $exception = new SkippedWithMessageException($skip); - } else { - $exception = new SkippedTestError($skip); - } - $failEvent = new FailEvent($test, $exception, 0); - $result->addSkipped($failEvent); - $this->dispatcher->dispatch($failEvent, Events::TEST_SKIPPED); + if ($test instanceof TestInterface && $test->getMetadata()->isBlocked()) { + $result->addTest($test); + $skip = $test->getMetadata()->getSkip(); + if ($skip !== null) { + if (class_exists(SkippedWithMessageException::class)) { + $exception = new SkippedWithMessageException($skip); + } else { + $exception = new SkippedTestError($skip); } - $incomplete = $test->getMetadata()->getIncomplete(); - if ($incomplete !== null) { - $exception = new IncompleteTestError($incomplete); - $failEvent = new FailEvent($test, $exception, 0); - $result->addIncomplete($failEvent); - $this->dispatcher->dispatch($failEvent, Events::TEST_INCOMPLETE); - } - - $this->dispatcher->dispatch(new TestEvent($test, 0), Events::TEST_END); - continue; + $failEvent = new FailEvent($test, $exception, 0); + $result->addSkipped($failEvent); + $this->dispatcher->dispatch($failEvent, Events::TEST_SKIPPED); + } + $incomplete = $test->getMetadata()->getIncomplete(); + if ($incomplete !== null) { + $exception = new IncompleteTestError($incomplete); + $failEvent = new FailEvent($test, $exception, 0); + $result->addIncomplete($failEvent); + $this->dispatcher->dispatch($failEvent, Events::TEST_INCOMPLETE); } + $this->dispatcher->dispatch(new TestEvent($test, 0), Events::TEST_END); + continue; } if ($test instanceof TestCaseWrapper) { @@ -158,7 +156,7 @@ protected function getDependencies(Test $test): array $tests = []; foreach ($test->fetchDependencies() as $requiredTestName) { $required = $this->findMatchedTest($requiredTestName); - if ($required === null) { + if (!$required instanceof Test) { continue; } $tests = array_merge($tests, $this->getDependencies($required)); @@ -208,10 +206,8 @@ public function setBaseName(string $baseName): void protected function fire(string $eventType, TestEvent $event): void { $test = $event->getTest(); - if ($test instanceof TestInterface) { - foreach ($test->getMetadata()->getGroups() as $group) { - $this->dispatcher->dispatch($event, $eventType . '.' . $group); - } + foreach ($test->getMetadata()->getGroups() as $group) { + $this->dispatcher->dispatch($event, $eventType . '.' . $group); } $this->dispatcher->dispatch($event, $eventType); } @@ -247,8 +243,8 @@ public function initPHPUnitConfiguration(): void $cliParameters [] = '--disallow-test-output'; } - $cliConfiguration = (new \PHPUnit\TextUI\CliArguments\Builder())->fromParameters($cliParameters, []); - $xmlConfiguration = \PHPUnit\TextUI\XmlConfiguration\DefaultConfiguration::create(); + $cliConfiguration = (new Builder())->fromParameters($cliParameters); + $xmlConfiguration = DefaultConfiguration::create(); Registry::init($cliConfiguration, $xmlConfiguration); } } diff --git a/src/Codeception/SuiteManager.php b/src/Codeception/SuiteManager.php index ae229a81bb..4b19a15f7b 100644 --- a/src/Codeception/SuiteManager.php +++ b/src/Codeception/SuiteManager.php @@ -25,8 +25,6 @@ class SuiteManager protected ?Suite $suite = null; - protected ?EventDispatcher $dispatcher = null; - protected GroupManager $groupManager; protected ModuleContainer $moduleContainer; @@ -39,10 +37,9 @@ class SuiteManager private Filter $testFilter; - public function __construct(EventDispatcher $dispatcher, string $name, array $settings, array $options) + public function __construct(protected ?EventDispatcher $dispatcher, string $name, array $settings, array $options) { $this->settings = $settings; - $this->dispatcher = $dispatcher; $this->di = new Di(); $this->groupManager = new GroupManager($settings['groups']); $this->moduleContainer = new ModuleContainer($this->di, $settings); @@ -116,10 +113,10 @@ protected function addToSuite(Test $test): void } $this->suite->addTest($test); - - if (!empty($groups) && $test instanceof TestInterface) { - $test->getMetadata()->setGroups($groups); + if ($groups === []) { + return; } + $test->getMetadata()->setGroups($groups); } protected function createSuite(string $name): Suite @@ -166,7 +163,7 @@ public function getModuleContainer(): ModuleContainer protected function checkEnvironmentExists(TestInterface $test): void { $envs = $test->getMetadata()->getEnv(); - if (empty($envs)) { + if ($envs === []) { return; } if (!isset($this->settings['env'])) { @@ -184,13 +181,13 @@ protected function checkEnvironmentExists(TestInterface $test): void protected function isExecutedInCurrentEnvironment(TestInterface $test): bool { $envs = $test->getMetadata()->getEnv(); - if (empty($envs)) { + if ($envs === []) { return true; } $currentEnvironments = explode(',', $this->env); foreach ($envs as $envList) { $envList = explode(',', $envList); - if (count($envList) == count(array_intersect($currentEnvironments, $envList))) { + if (count($envList) === count(array_intersect($currentEnvironments, $envList))) { return true; } } diff --git a/src/Codeception/Template/Bootstrap.php b/src/Codeception/Template/Bootstrap.php index 6672ca35c8..ed4f247b91 100644 --- a/src/Codeception/Template/Bootstrap.php +++ b/src/Codeception/Template/Bootstrap.php @@ -33,7 +33,7 @@ public function setup(): void $input = $this->input; if ($input->getOption('namespace')) { - $this->namespace = trim($input->getOption('namespace'), '\\'); + $this->namespace = trim((string) $input->getOption('namespace'), '\\'); } if ($input->hasOption('actor') && $input->getOption('actor')) { diff --git a/src/Codeception/Template/Dependencies.php b/src/Codeception/Template/Dependencies.php index 79368cad59..1bb1972cc8 100644 --- a/src/Codeception/Template/Dependencies.php +++ b/src/Codeception/Template/Dependencies.php @@ -27,7 +27,7 @@ public function setup(): void $config = Configuration::config(); $modules = []; $suites = Configuration::suites(); - if (empty($suites)) { + if ($suites === []) { $this->sayError("No suites found in current config."); $this->sayWarning('If you use sub-configs with `include` option, run this script on subconfigs:'); $this->sayWarning('Example: php vendor/bin/codecept init dependencies -c backend/'); diff --git a/src/Codeception/Test/DataProvider.php b/src/Codeception/Test/DataProvider.php index 157583f59f..827279777f 100644 --- a/src/Codeception/Test/DataProvider.php +++ b/src/Codeception/Test/DataProvider.php @@ -45,7 +45,7 @@ public static function getDataForMethod(ReflectionMethod $method, ?ReflectionCla // dataProvider annotation $dataProviderAnnotations = Annotation::forMethod($testClassName, $methodName)->fetchAll('dataProvider'); // lowercase for back compatible - if (empty($dataProviderAnnotations)) { + if ($dataProviderAnnotations === []) { $dataProviderAnnotations = Annotation::forMethod($testClassName, $methodName)->fetchAll('dataprovider'); } @@ -136,8 +136,8 @@ private static function getTestClass(ReflectionMethod $dataProviderMethod, ?Refl { $dataProviderDeclaringClass = $dataProviderMethod->getDeclaringClass(); // data provider in abstract class? - if ($dataProviderDeclaringClass->isAbstract() && null !== $testClass && $dataProviderDeclaringClass->name !== $testClass->name) { - $dataProviderDeclaringClass = $testClass; + if ($dataProviderDeclaringClass->isAbstract() && $testClass instanceof ReflectionClass && $dataProviderDeclaringClass->name !== $testClass->name) { + return $testClass; } return $dataProviderDeclaringClass; } diff --git a/src/Codeception/Test/Feature/ScenarioLoader.php b/src/Codeception/Test/Feature/ScenarioLoader.php index c9e94fb80f..62eda58147 100644 --- a/src/Codeception/Test/Feature/ScenarioLoader.php +++ b/src/Codeception/Test/Feature/ScenarioLoader.php @@ -34,7 +34,7 @@ public function getScenarioText(string $format = 'text'): string $code = $this->getSourceCode(); $this->getParser()->parseFeature($code); $this->getParser()->parseSteps($code); - if ($format == 'html') { + if ($format === 'html') { return $this->getScenario()->getHtml(); } return $this->getScenario()->getText(); diff --git a/src/Codeception/Test/Filter.php b/src/Codeception/Test/Filter.php index a7400e318a..ac00bf7c3d 100644 --- a/src/Codeception/Test/Filter.php +++ b/src/Codeception/Test/Filter.php @@ -4,6 +4,8 @@ namespace Codeception\Test; +use function array_intersect; + class Filter { private ?string $namePattern = null; @@ -13,11 +15,11 @@ class Filter /** * @param string[] $includeGroups * @param string[] $excludeGroups - * @param string $namePattern + * @param string|null $namePattern */ public function __construct( - private ?array $includeGroups, - private ?array $excludeGroups, + private readonly ?array $includeGroups, + private readonly ?array $excludeGroups, ?string $namePattern ) { if ($namePattern === null) { @@ -25,7 +27,7 @@ public function __construct( } // Validates regexp without E_WARNING - set_error_handler(function () { + set_error_handler(function (): void { }, E_WARNING); $isRegularExpression = preg_match($namePattern, '') !== false; restore_error_handler(); @@ -95,10 +97,10 @@ public function isNameAccepted(Test $test): bool public function isGroupAccepted(Test $test, array $groups): bool { - if ($this->includeGroups !== null && $this->includeGroups !== [] && count(\array_intersect($groups, $this->includeGroups)) === 0) { + if ($this->includeGroups !== null && $this->includeGroups !== [] && array_intersect($groups, $this->includeGroups) === []) { return false; } - if ($this->excludeGroups !== null && $this->excludeGroups !== [] && count(\array_intersect($groups, $this->excludeGroups)) > 0) { + if ($this->excludeGroups !== null && $this->excludeGroups !== [] && count(array_intersect($groups, $this->excludeGroups)) > 0) { return false; } diff --git a/src/Codeception/Test/Gherkin.php b/src/Codeception/Test/Gherkin.php index 785ddef917..fb2639fc05 100644 --- a/src/Codeception/Test/Gherkin.php +++ b/src/Codeception/Test/Gherkin.php @@ -33,24 +33,15 @@ class Gherkin extends Test implements ScenarioDriven, Reported { - protected array $steps = []; - - protected FeatureNode $featureNode; - - protected ScenarioInterface $scenarioNode; - protected Scenario $scenario; - public function __construct(FeatureNode $featureNode, ScenarioInterface $scenarioNode, array $steps = []) + public function __construct(protected FeatureNode $featureNode, protected ScenarioInterface $scenarioNode, protected array $steps = []) { - $this->featureNode = $featureNode; - $this->scenarioNode = $scenarioNode; - $this->steps = $steps; $this->setMetadata(new Metadata()); $this->scenario = new Scenario($this); - $this->getMetadata()->setName($scenarioNode->getTitle()); - $this->getMetadata()->setFeature((string)$featureNode->getTitle()); - $this->getMetadata()->setFilename($featureNode->getFile()); + $this->getMetadata()->setName($this->scenarioNode->getTitle()); + $this->getMetadata()->setFeature((string)$this->featureNode->getTitle()); + $this->getMetadata()->setFilename($this->featureNode->getFile()); } public function __clone(): void diff --git a/src/Codeception/Test/Loader.php b/src/Codeception/Test/Loader.php index 9391d2e3f5..421572d0b8 100644 --- a/src/Codeception/Test/Loader.php +++ b/src/Codeception/Test/Loader.php @@ -106,10 +106,10 @@ public function getTests(): array private function splitTestsIntoChunks(int $chunks): array { - if (empty($this->tests)) { + if ($this->tests === []) { return []; } - return array_chunk($this->tests, intval(ceil(sizeof($this->tests) / $chunks))); + return array_chunk($this->tests, (int) ceil(count($this->tests) / $chunks)); } protected function relativeName(string $file): string diff --git a/src/Codeception/Test/Loader/Gherkin.php b/src/Codeception/Test/Loader/Gherkin.php index d079dd6af8..1c0a9bc65b 100644 --- a/src/Codeception/Test/Loader/Gherkin.php +++ b/src/Codeception/Test/Loader/Gherkin.php @@ -10,7 +10,6 @@ use Behat\Gherkin\Node\ExampleNode; use Behat\Gherkin\Node\FeatureNode; use Behat\Gherkin\Node\OutlineNode; -use Behat\Gherkin\Node\ScenarioInterface; use Behat\Gherkin\Node\ScenarioNode; use Behat\Gherkin\Parser as GherkinParser; use Codeception\Configuration; @@ -93,7 +92,7 @@ protected function fetchGherkinSteps(): void $this->addSteps($roleContexts, "role:{$role}"); } - if (empty($this->steps) && empty($contexts['default']) && $this->settings['actor']) { // if no context is set, actor to be a context + if ($this->steps === [] && empty($contexts['default']) && $this->settings['actor']) { // if no context is set, actor to be a context $actorContext = $this->supportNamespace() . $this->settings['actor']; if ($actorContext) { $contexts['default'][] = $actorContext; @@ -197,7 +196,6 @@ public function loadTests(string $filename): void } foreach ($featureNode->getScenarios() as $scenarioNode) { - /** @var ScenarioInterface $scenarioNode */ $steps = $this->steps['default']; // load default context foreach (array_merge($scenarioNode->getTags(), $featureNode->getTags()) as $tag) { // load tag contexts diff --git a/src/Codeception/Test/Loader/Unit.php b/src/Codeception/Test/Loader/Unit.php index 2c674ab882..8717bf8802 100644 --- a/src/Codeception/Test/Loader/Unit.php +++ b/src/Codeception/Test/Loader/Unit.php @@ -111,8 +111,6 @@ protected function enhancePhpunitTest( array $afterClassMethods, ): TestCaseWrapper { - $test = new TestCaseWrapper($testCase, $beforeClassMethods, $afterClassMethods); - - return $test; + return new TestCaseWrapper($testCase, $beforeClassMethods, $afterClassMethods); } } diff --git a/src/Codeception/Test/Metadata.php b/src/Codeception/Test/Metadata.php index ef4e2961be..25c61c9c25 100644 --- a/src/Codeception/Test/Metadata.php +++ b/src/Codeception/Test/Metadata.php @@ -131,6 +131,7 @@ public function setFilename(string $filename): void $this->filename = $filename; } + /** @return string[] */ public function getDependencies(): array { return $this->params['depends']; @@ -138,7 +139,10 @@ public function getDependencies(): array public function isBlocked(): bool { - return $this->getSkip() !== null || $this->getIncomplete() !== null; + if ($this->getSkip() !== null) { + return true; + } + return $this->getIncomplete() !== null; } public function getFeature(): string @@ -214,7 +218,7 @@ public function setParamsFromAttributes($attributes): void { $params = []; foreach ($attributes as $attribute) { - $name = lcfirst(str_replace('Codeception\\Attribute\\', '', $attribute->getName())); + $name = lcfirst(str_replace('Codeception\\Attribute\\', '', (string) $attribute->getName())); if ($attribute->isRepeated()) { $params[$name] ??= []; $params[$name][] = $attribute->getArguments(); @@ -233,7 +237,7 @@ public function setParamsFromAttributes($attributes): void continue; }; - $this->params[$single] = array_map(fn($a) => is_array($a) ? $a : [$a], $this->params[$single]); + $this->params[$single] = array_map(fn($a): array => is_array($a) ? $a : [$a], $this->params[$single]); $this->params[$single] = array_merge(...$this->params[$single]); } diff --git a/src/Codeception/Test/Test.php b/src/Codeception/Test/Test.php index fbfa1a1385..dd26585eab 100644 --- a/src/Codeception/Test/Test.php +++ b/src/Codeception/Test/Test.php @@ -10,8 +10,8 @@ use Codeception\Exception\UselessTestException; use Codeception\PHPUnit\Wrapper\Test as TestWrapper; use Codeception\ResultAggregator; -use Codeception\Test\Interfaces\ScenarioDriven; use Codeception\TestInterface; +use LogicException; use PHPUnit\Framework\Assert; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\Exception; @@ -186,11 +186,7 @@ final public function realRun(ResultAggregator $result): void $result->addFailure(new FailEvent($this, $e, $time)); $status = self::STATUS_FAIL; $eventType = Events::TEST_FAIL; - } catch (Exception $e) { - $result->addError(new FailEvent($this, $e, $time)); - $status = self::STATUS_ERROR; - $eventType = Events::TEST_ERROR; - } catch (Throwable $e) { + } catch (Exception | Throwable $e) { $result->addError(new FailEvent($this, $e, $time)); $status = self::STATUS_ERROR; $eventType = Events::TEST_ERROR; @@ -240,8 +236,8 @@ protected function doesNotPerformAssertions(): bool public function getResultAggregator(): ResultAggregator { - if ($this->resultAggregator === null) { - throw new \LogicException('ResultAggregator is not set'); + if (!$this->resultAggregator instanceof ResultAggregator) { + throw new LogicException('ResultAggregator is not set'); } return $this->resultAggregator; } @@ -270,14 +266,12 @@ public function numberOfAssertionsPerformed(): int protected function fire(string $eventType, TestEvent $event): void { - if ($this->eventDispatcher === null) { + if (!$this->eventDispatcher instanceof EventDispatcher) { throw new RuntimeException('EventDispatcher must be injected before running test'); } $test = $event->getTest(); - if ($test instanceof TestInterface) { - foreach ($test->getMetadata()->getGroups() as $group) { - $this->eventDispatcher->dispatch($event, $eventType . '.' . $group); - } + foreach ($test->getMetadata()->getGroups() as $group) { + $this->eventDispatcher->dispatch($event, $eventType . '.' . $group); } $this->eventDispatcher->dispatch($event, $eventType); } @@ -301,7 +295,7 @@ private function checkConditionalAsserts(ResultAggregator $result): void } $lastFailure = $result->getLastFailure(); - if ($lastFailure === null) { + if (!$lastFailure instanceof FailEvent) { return; } diff --git a/src/Codeception/Test/TestCaseWrapper.php b/src/Codeception/Test/TestCaseWrapper.php index dad82a972c..e10b5984d8 100644 --- a/src/Codeception/Test/TestCaseWrapper.php +++ b/src/Codeception/Test/TestCaseWrapper.php @@ -25,7 +25,7 @@ */ class TestCaseWrapper extends Test implements Reported, Dependent, StrictCoverage, TestInterface, Descriptive { - private Metadata $metadata; + private readonly Metadata $metadata; /** * @var array @@ -44,11 +44,7 @@ public function __construct( $this->metadata = new Metadata(); $metadata = $this->metadata; - if (PHPUnitVersion::series() < 10) { - $methodName = $testCase->getName(false); - } else { - $methodName = $testCase->name(); - } + $methodName = PHPUnitVersion::series() < 10 ? $testCase->getName(false) : $testCase->name(); $metadata->setName($methodName); $metadata->setFilename((new ReflectionClass($testCase))->getFileName()); @@ -156,18 +152,15 @@ public function test(): void } $numberOfAssertionsPerformed = $this->getNumAssertions(); - if ( - $this->reportUselessTests && - $numberOfAssertionsPerformed > 0 && - $this->testCase->doesNotPerformAssertions() - ) { - throw new UselessTestException( - sprintf( - 'This test indicates it does not perform assertions but %d assertions were performed', - $numberOfAssertionsPerformed - ) - ); + if (!$this->reportUselessTests || $numberOfAssertionsPerformed <= 0 || !$this->testCase->doesNotPerformAssertions()) { + return; } + throw new UselessTestException( + sprintf( + 'This test indicates it does not perform assertions but %d assertions were performed', + $numberOfAssertionsPerformed + ) + ); } /** diff --git a/src/Codeception/Test/Unit.php b/src/Codeception/Test/Unit.php index c4fc0066b1..072dacddcc 100644 --- a/src/Codeception/Test/Unit.php +++ b/src/Codeception/Test/Unit.php @@ -16,8 +16,8 @@ use Codeception\Test\Feature\Stub; use Codeception\TestInterface; use Codeception\Util\Debug; +use LogicException; -use function get_class; use function lcfirst; use function method_exists; @@ -38,14 +38,14 @@ class Unit extends TestCase implements public function __clone(): void { - if ($this->scenario !== null) { + if ($this->scenario instanceof Scenario) { $this->scenario = clone $this->scenario; } } public function getMetadata(): Metadata { - if (!$this->metadata) { + if (!$this->metadata instanceof Metadata) { $this->metadata = new Metadata(); } return $this->metadata; @@ -63,7 +63,7 @@ public function setMetadata(?Metadata $metadata): void public function getResultAggregator(): ResultAggregator { - throw new \LogicException('This method should not be called, TestCaseWrapper class must be used instead'); + throw new LogicException('This method should not be called, TestCaseWrapper class must be used instead'); } protected function _setUp() @@ -81,7 +81,7 @@ protected function _setUp() /** @var Di $di */ $di = $this->getMetadata()->getService('di'); // auto-inject $tester property - if (($this->getMetadata()->getCurrent('actor')) && ($property = lcfirst(Configuration::config()['actor_suffix']))) { + if (($this->getMetadata()->getCurrent('actor')) && ($property = lcfirst((string) Configuration::config()['actor_suffix']))) { $this->$property = $di->instantiate($this->getMetadata()->getCurrent('actor')); } @@ -124,7 +124,6 @@ public function getModule(string $module): Module * Starts interactive pause in this test * * @param array $vars - * @return void */ public function pause(array $vars = []): void { @@ -149,7 +148,7 @@ public function getReportFields(): array { return [ 'name' => $this->getName(false), - 'class' => get_class($this), + 'class' => static::class, 'file' => $this->getMetadata()->getFilename() ]; } @@ -158,8 +157,8 @@ public function fetchDependencies(): array { $names = []; foreach ($this->getMetadata()->getDependencies() as $required) { - if (!str_contains($required, ':') && method_exists($this, $required)) { - $required = get_class($this) . ":{$required}"; + if (!str_contains((string) $required, ':') && method_exists($this, $required)) { + $required = static::class . ":{$required}"; } $names[] = $required; } diff --git a/src/Codeception/Util/ActionSequence.php b/src/Codeception/Util/ActionSequence.php index df341382dc..f6efadacd6 100644 --- a/src/Codeception/Util/ActionSequence.php +++ b/src/Codeception/Util/ActionSequence.php @@ -7,10 +7,10 @@ use Closure; use Codeception\Step\Action; use Exception; +use Stringable; use function call_user_func_array; use function codecept_debug; -use function get_class; use function implode; use function is_array; use function str_replace; @@ -53,7 +53,7 @@ * @method $this seeOptionIsSelected($selector, $optionText) * @method $this dontSeeOptionIsSelected($selector, $optionText) */ -class ActionSequence +class ActionSequence implements Stringable { /** * @var Action[] @@ -114,7 +114,7 @@ public function run(object $context): void try { call_user_func_array([$context, $step->getAction()], $step->getArguments()); } catch (Exception $exception) { - $class = get_class($exception); // rethrow exception for a specific action + $class = $exception::class; // rethrow exception for a specific action throw new $class($exception->getMessage() . "\nat {$step}"); } } diff --git a/src/Codeception/Util/Annotation.php b/src/Codeception/Util/Annotation.php index c4975ad799..b9b7474700 100644 --- a/src/Codeception/Util/Annotation.php +++ b/src/Codeception/Util/Annotation.php @@ -4,11 +4,11 @@ namespace Codeception\Util; +use ReflectionAttribute; use ReflectionClass; use ReflectionMethod; use Reflector; -use function get_class; use function in_array; use function is_object; use function json_decode; @@ -52,7 +52,7 @@ class Annotation public static function forClass(object|string $class): self { if (is_object($class)) { - $class = get_class($class); + $class = $class::class; } if (!isset(static::$reflectedClasses[$class])) { @@ -111,9 +111,9 @@ public function method(string $method): self public function fetch(string $annotation): ?string { $attr = $this->attribute($annotation); - if ($attr) { + if ($attr instanceof ReflectionAttribute) { $arguments = $attr->getArguments(); - if (count($arguments) === 0) { + if ($arguments === []) { return ''; } return $arguments[0]; @@ -128,7 +128,7 @@ public function fetch(string $annotation): ?string public function fetchAll(string $annotation): array { $attr = $this->attribute($annotation); - if ($attr) { + if ($attr instanceof ReflectionAttribute) { if (!$attr->isRepeated()) { return $attr->getArguments(); } @@ -137,11 +137,11 @@ public function fetchAll(string $annotation): array $annotation = 'examples'; // we renamed this annotation } $name = ucfirst($annotation); - $attrs = array_filter($attrs, fn ($a) => $a->getName() === "Codeception\\Attribute\\$name"); + $attrs = array_filter($attrs, fn ($a): bool => $a->getName() === "Codeception\\Attribute\\$name"); if ($annotation === 'examples') { - return array_map(fn (\ReflectionAttribute $a) => $a->getArguments(), $attrs); + return array_map(fn (ReflectionAttribute $a): array => $a->getArguments(), $attrs); } - return array_merge(...array_map(fn (\ReflectionAttribute $a) => $a->getArguments(), $attrs)); + return array_merge(...array_map(fn (ReflectionAttribute $a): array => $a->getArguments(), $attrs)); } $docBlock = (string)$this->currentReflectedItem->getDocComment(); if (preg_match_all(sprintf(self::$regex, $annotation), $docBlock, $matched)) { @@ -154,19 +154,18 @@ public function attributes(): array { $attrs = $this->currentReflectedItem->getAttributes(); $attrs = array_filter($attrs); - $attrs = array_filter($attrs, fn (\ReflectionAttribute $a) => str_starts_with($a->getName(), 'Codeception\\Attribute\\')); - return $attrs; + return array_filter($attrs, fn (ReflectionAttribute $a): bool => str_starts_with($a->getName(), 'Codeception\\Attribute\\')); } - public function attribute($name): ?\ReflectionAttribute + public function attribute(string $name): ?ReflectionAttribute { $attrs = $this->attributes(); if ($name === 'example') { $name = 'examples'; // we renamed this annotation } $name = ucfirst($name); - $attrs = array_filter($attrs, fn ($a) => $a->getName() === "Codeception\\Attribute\\$name"); - if (empty($attrs)) { + $attrs = array_filter($attrs, fn ($a): bool => $a->getName() === "Codeception\\Attribute\\$name"); + if ($attrs === []) { return null; } return reset($attrs); diff --git a/src/Codeception/Util/Autoload.php b/src/Codeception/Util/Autoload.php index 834c0f32b7..e04b96c6a0 100644 --- a/src/Codeception/Util/Autoload.php +++ b/src/Codeception/Util/Autoload.php @@ -53,7 +53,7 @@ private function __construct() public static function addNamespace(string $prefix, string $baseDir, bool $prepend = false): void { if (!self::$registered) { - spl_autoload_register([__CLASS__, 'load']); + spl_autoload_register(fn(string $class): string|false => self::load($class)); self::$registered = true; } @@ -101,7 +101,7 @@ public static function load(string $class): string|false } // fix for empty prefix - if (isset(self::$map['\\']) && ($class[0] != '\\')) { + if (isset(self::$map['\\']) && ($class[0] !== '\\')) { return self::load('\\' . $class); } diff --git a/src/Codeception/Util/Debug.php b/src/Codeception/Util/Debug.php index cf2d3ab616..b2e54753d2 100644 --- a/src/Codeception/Util/Debug.php +++ b/src/Codeception/Util/Debug.php @@ -4,7 +4,6 @@ namespace Codeception\Util; -use Codeception\Command\Console; use Codeception\Lib\Console\Output; use Codeception\Lib\PauseShell; use Symfony\Component\Console\Helper\QuestionHelper; @@ -29,7 +28,7 @@ public static function setOutput(Output $output): void */ public static function debug(mixed $message): void { - if (!self::$output) { + if (!self::$output instanceof Output) { return; } self::$output->debug($message); @@ -71,7 +70,7 @@ public static function pause(array $vars = []): void public static function confirm($question) { - if (!self::$output) { + if (!self::$output instanceof Output) { return; } diff --git a/src/Codeception/Util/FileSystem.php b/src/Codeception/Util/FileSystem.php index 39c7b51daa..e5fe135d46 100644 --- a/src/Codeception/Util/FileSystem.php +++ b/src/Codeception/Util/FileSystem.php @@ -60,9 +60,7 @@ public static function deleteDir(string $dir): bool if (!self::deleteDir($dir . DIRECTORY_SEPARATOR . $item)) { chmod($dir . DIRECTORY_SEPARATOR . $item, 0777); - if (!self::deleteDir($dir . DIRECTORY_SEPARATOR . $item)) { - return false; - } + return false; } } @@ -74,7 +72,7 @@ public static function copyDir(string $src, string $dst): void $dir = opendir($src); @mkdir($dst); while (false !== ($file = readdir($dir))) { - if (($file != '.') && ($file != '..')) { + if (($file !== '.') && ($file !== '..')) { if (is_dir($src . DIRECTORY_SEPARATOR . $file)) { self::copyDir($src . DIRECTORY_SEPARATOR . $file, $dst . DIRECTORY_SEPARATOR . $file); } else { diff --git a/src/Codeception/Util/PathResolver.php b/src/Codeception/Util/PathResolver.php index 389ad572cf..9e41e0f1b4 100644 --- a/src/Codeception/Util/PathResolver.php +++ b/src/Codeception/Util/PathResolver.php @@ -128,7 +128,7 @@ private static function getPathAbsolutenessPrefix(string $path, string $dirSep = */ private static function isWindows(string $dirSep = DIRECTORY_SEPARATOR): bool { - return ($dirSep == '\\'); + return ($dirSep === '\\'); } public static function isPathAbsolute(string $path): bool diff --git a/src/Codeception/Util/ReflectionHelper.php b/src/Codeception/Util/ReflectionHelper.php index 501fbd4917..44096e62dd 100644 --- a/src/Codeception/Util/ReflectionHelper.php +++ b/src/Codeception/Util/ReflectionHelper.php @@ -16,7 +16,6 @@ use function array_pop; use function count; use function explode; -use function get_class; use function implode; use function in_array; use function is_array; @@ -90,7 +89,7 @@ public static function invokePrivateMethod(?object $object, string $method, arra */ public static function getClassShortName(object $object): string { - $path = explode('\\', get_class($object)); + $path = explode('\\', $object::class); return array_pop($path); } @@ -119,7 +118,7 @@ public static function getDefaultValue(ReflectionParameter $parameter): string { if ($parameter->isDefaultValueAvailable()) { if (method_exists($parameter, 'isDefaultValueConstant') && $parameter->isDefaultValueConstant()) { - $constName = $parameter->getDefaultValueConstantName(); + $constName = (string)$parameter->getDefaultValueConstantName(); if (str_contains($constName, '::')) { [$class, $const] = explode('::', $constName); if (in_array($class, ['self', 'static'])) { @@ -176,7 +175,7 @@ public static function phpEncodeArray(array $array): string ); if ($isPlainArray($array)) { - return '[' . implode(', ', array_map([self::class, 'phpEncodeValue'], $array)) . ']'; + return '[' . implode(', ', array_map(fn($value): string => self::phpEncodeValue($value), $array)) . ']'; } $values = array_map( diff --git a/src/Codeception/Util/StackTraceFilter.php b/src/Codeception/Util/StackTraceFilter.php index 801e771326..e607e86c1b 100644 --- a/src/Codeception/Util/StackTraceFilter.php +++ b/src/Codeception/Util/StackTraceFilter.php @@ -18,7 +18,7 @@ public static function getFilteredStackTrace(Throwable $e, bool $asString = true { $stackTrace = $asString ? '' : []; - $trace = $e->getPrevious() ? $e->getPrevious()->getTrace() : $e->getTrace(); + $trace = $e->getPrevious() instanceof Throwable ? $e->getPrevious()->getTrace() : $e->getTrace(); $eFile = $e->getFile(); $eLine = $e->getLine(); @@ -31,10 +31,10 @@ public static function getFilteredStackTrace(Throwable $e, bool $asString = true } foreach ($trace as $step) { - if (self::classIsFiltered($step) and $filter) { + if (self::classIsFiltered($step) && $filter) { continue; } - if (self::fileIsFiltered($step) and $filter) { + if (self::fileIsFiltered($step) && $filter) { continue; } @@ -60,13 +60,14 @@ protected static function classIsFiltered(array $step): bool $className = $step['class']; foreach (self::$filteredClassesPattern as $filteredClassName) { - if (str_starts_with($className, $filteredClassName)) { + if (str_starts_with((string) $className, (string) $filteredClassName)) { return true; } } return false; } + /** @param string[] $step */ protected static function fileIsFiltered(array $step): bool { if (!isset($step['file'])) { @@ -89,12 +90,7 @@ protected static function fileIsFiltered(array $step): bool if (str_contains($step['file'], $modulePath)) { return false; // don`t filter modules } - - if (str_contains($step['file'], 'src' . DIRECTORY_SEPARATOR . 'Codeception' . DIRECTORY_SEPARATOR)) { - return true; - } - - return false; + return str_contains($step['file'], 'src' . DIRECTORY_SEPARATOR . 'Codeception' . DIRECTORY_SEPARATOR); } private static function frameExists(array $trace, string $file, int $line): bool diff --git a/src/Codeception/Util/Template.php b/src/Codeception/Util/Template.php index ab450a39e2..dd2c536984 100644 --- a/src/Codeception/Util/Template.php +++ b/src/Codeception/Util/Template.php @@ -20,9 +20,9 @@ class Template public function __construct( private string $template, - private string $placeholderStart = '{{', - private string $placeholderEnd = '}}', - private ?string $encoderFunction = null, + private readonly string $placeholderStart = '{{', + private readonly string $placeholderEnd = '}}', + private readonly ?string $encoderFunction = null, ) { } @@ -45,10 +45,7 @@ public function setVars(array $vars): void public function getVar(string $name) { - if (isset($this->vars[$name])) { - return $this->vars[$name]; - } - return null; + return $this->vars[$name] ?? null; } /** diff --git a/tests/cli/BuildCest.php b/tests/cli/BuildCest.php index caf67ae1d5..757e76b1ca 100644 --- a/tests/cli/BuildCest.php +++ b/tests/cli/BuildCest.php @@ -8,7 +8,6 @@ #[Group('core')] final class BuildCest { - /** @var string */ private string $originalCliHelperContents; public function _before() @@ -21,7 +20,7 @@ public function _after() file_put_contents(codecept_root_dir('tests/support/CliHelper.php'), $this->originalCliHelperContents); } - public function buildsActionsForAClass(CliGuy $I): void + public function buildsActionsForAClass(CliGuy $I) { $I->wantToTest('build command'); $I->runShellCommand('php codecept build'); @@ -36,7 +35,7 @@ public function buildsActionsForAClass(CliGuy $I): void $I->seeInThisFile('public function assertSame($expected, $actual, string $message = "") {'); } - public function usesLiteralTypes(CliGuy $I, Scenario $scenario): void + public function usesLiteralTypes(CliGuy $I, Scenario $scenario) { $I->wantToTest('generate typehints with generated actions'); @@ -55,7 +54,7 @@ public function usesLiteralTypes(CliGuy $I, Scenario $scenario): void $I->seeInThisFile('public function grabFromOutput(string $regex): string'); } - public function generatedUnionReturnType(CliGuy $I, Scenario $scenario): void + public function generatedUnionReturnType(CliGuy $I, Scenario $scenario) { $I->wantToTest('generate action with union return type'); @@ -72,12 +71,8 @@ public function generatedUnionReturnType(CliGuy $I, Scenario $scenario): void $I->seeInThisFile('public function grabFromOutput(array|string $param): string|int'); } - public function generatedIntersectReturnTypeOnPhp81(CliGuy $I, Scenario $scenario): void + public function generatedIntersectReturnTypeOnPhp81(CliGuy $I, Scenario $scenario) { - if (PHP_VERSION_ID < 80100) { - $scenario->skip('Does not work in PHP < 8.1'); - } - $I->wantToTest('generate action with intersect return type'); $cliHelperContents = file_get_contents(codecept_root_dir('tests/support/CliHelper.php')); @@ -93,7 +88,7 @@ public function generatedIntersectReturnTypeOnPhp81(CliGuy $I, Scenario $scenari $I->seeInThisFile('public function grabFromOutput(\Codeception\Module\CliHelper&\ArrayObject $param): \Codeception\Module\CliHelper&\ArrayObject'); } - public function noReturnForVoidType(CliGuy $I, Scenario $scenario): void + public function noReturnForVoidType(CliGuy $I, Scenario $scenario) { $I->wantToTest('no return keyword generated for void typehint'); @@ -112,7 +107,7 @@ public function noReturnForVoidType(CliGuy $I, Scenario $scenario): void $I->dontSeeInThisFile('return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion(\'seeDirFound\', func_get_args()));'); } - public function generateNullableParameters(CliGuy $I, Scenario $scenario): void + public function generateNullableParameters(CliGuy $I, Scenario $scenario) { $cliHelperContents = file_get_contents(codecept_root_dir('tests/support/CliHelper.php')); $cliHelperContents = str_replace('public function seeDirFound($dir)', 'public function seeDirFound(\Directory $dir = null): ?bool', $cliHelperContents); @@ -127,7 +122,7 @@ public function generateNullableParameters(CliGuy $I, Scenario $scenario): void $I->seeInThisFile('public function seeDirFound(?\Directory $dir = NULL): ?bool'); } - public function generateMixedParameters(CliGuy $I, Scenario $scenario): void + public function generateMixedParameters(CliGuy $I, Scenario $scenario) { $cliHelperContents = file_get_contents(codecept_root_dir('tests/support/CliHelper.php')); $cliHelperContents = str_replace('public function seeDirFound($dir)', 'public function seeDirFound(mixed $dir = null): mixed', $cliHelperContents); @@ -142,7 +137,7 @@ public function generateMixedParameters(CliGuy $I, Scenario $scenario): void $I->seeInThisFile('public function seeDirFound(mixed $dir = NULL): mixed'); } - public function generateCorrectTypeWhenSelfTypeIsUsed(CliGuy $I, Scenario $scenario): void + public function generateCorrectTypeWhenSelfTypeIsUsed(CliGuy $I, Scenario $scenario) { if (PHP_MAJOR_VERSION < 7) { $scenario->skip('Does not work in PHP < 7'); @@ -160,7 +155,7 @@ public function generateCorrectTypeWhenSelfTypeIsUsed(CliGuy $I, Scenario $scena $I->seeInThisFile('public function seeDirFound(\Codeception\Module\CliHelper $dir): \Codeception\Module\CliHelper'); } - public function generateCorrectTypeWhenParentTypeIsUsed(CliGuy $I, Scenario $scenario): void + public function generateCorrectTypeWhenParentTypeIsUsed(CliGuy $I, Scenario $scenario) { if (PHP_MAJOR_VERSION < 7) { $scenario->skip('Does not work in PHP < 7'); @@ -178,12 +173,8 @@ public function generateCorrectTypeWhenParentTypeIsUsed(CliGuy $I, Scenario $sce $I->seeInThisFile('public function seeDirFound(\Codeception\Module $dir): \Codeception\Module'); } - public function noReturnForNeverType(CliGuy $I, Scenario $scenario): void + public function noReturnForNeverType(CliGuy $I, Scenario $scenario) { - if (PHP_VERSION_ID < 80100) { - $scenario->skip('Does not work in PHP < 8.1'); - } - $I->wantToTest('no return keyword generated for never typehint'); $cliHelperContents = file_get_contents(codecept_root_dir('tests/support/CliHelper.php')); @@ -201,7 +192,7 @@ public function noReturnForNeverType(CliGuy $I, Scenario $scenario): void $I->dontSeeInThisFile('return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion(\'seeDirFound\', func_get_args()));'); } - public function generateAttributeForMethodAttributeWithNoParametersAndNoBrackets(CliGuy $I, Scenario $scenario): void + public function generateAttributeForMethodAttributeWithNoParametersAndNoBrackets(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for method attribute with no parameters and no brackets'); @@ -219,7 +210,7 @@ public function generateAttributeForMethodAttributeWithNoParametersAndNoBrackets $I->seeInThisFile("*/\n #[\Codeception\Attribute\Examples()]\n public function canSeeDirFound(\$dir) {"); } - public function generateAttributeForMethodAttributeWithNoParametersAndEmptyBrackets(CliGuy $I, Scenario $scenario): void + public function generateAttributeForMethodAttributeWithNoParametersAndEmptyBrackets(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for method attribute with no parameters and empty brackets'); @@ -237,7 +228,7 @@ public function generateAttributeForMethodAttributeWithNoParametersAndEmptyBrack $I->seeInThisFile("*/\n #[\Codeception\Attribute\Examples()]\n public function canSeeDirFound(\$dir) {"); } - public function generateAttributeForMethodAttributeWithASingleParameter(CliGuy $I, Scenario $scenario): void + public function generateAttributeForMethodAttributeWithASingleParameter(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for method attribute with a single parameter'); @@ -255,7 +246,7 @@ public function generateAttributeForMethodAttributeWithASingleParameter(CliGuy $ $I->seeInThisFile("*/\n #[\Codeception\Attribute\Examples(\"magic\")]\n public function canSeeDirFound(\$dir) {"); } - public function generateAttributeForMethodAttributeWithMultipleParameters(CliGuy $I, Scenario $scenario): void + public function generateAttributeForMethodAttributeWithMultipleParameters(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for method attribute with multiple parameters'); @@ -273,7 +264,7 @@ public function generateAttributeForMethodAttributeWithMultipleParameters(CliGuy $I->seeInThisFile("*/\n #[\Codeception\Attribute\Examples(\"magic\", \"dark\")]\n public function canSeeDirFound(\$dir) {"); } - public function generateAttributeForMethodAttributeWithMultipleParametersOverMultipleLines(CliGuy $I, Scenario $scenario): void + public function generateAttributeForMethodAttributeWithMultipleParametersOverMultipleLines(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for method attribute with multiple parameters over multiple lines'); @@ -291,7 +282,7 @@ public function generateAttributeForMethodAttributeWithMultipleParametersOverMul $I->seeInThisFile("*/\n #[\Codeception\Attribute\Examples(\"magic\", \"dark\")]\n public function canSeeDirFound(\$dir) {"); } - public function generateAttributesForMultipleMethodAttributeDeclarations(CliGuy $I, Scenario $scenario): void + public function generateAttributesForMultipleMethodAttributeDeclarations(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for multiple method attribute declarations'); @@ -309,7 +300,7 @@ public function generateAttributesForMultipleMethodAttributeDeclarations(CliGuy $I->seeInThisFile("*/\n #[\Codeception\Attribute\Examples()]\n #[\Codeception\Attribute\Before(\"doX\")]\n public function canSeeDirFound(\$dir) {"); } - public function generateAttributeForParameterAttributeWithNoParametersAndNoBrackets(CliGuy $I, Scenario $scenario): void + public function generateAttributeForParameterAttributeWithNoParametersAndNoBrackets(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for parameter attribute with no parameters and no brackets'); @@ -327,7 +318,7 @@ public function generateAttributeForParameterAttributeWithNoParametersAndNoBrack $I->seeInThisFile("*/\n public function canSeeDirFound(#[\JetBrains\PhpStorm\Deprecated()] \$dir) {"); } - public function generateAttributeForParameterAttributeWithMultipleParameters(CliGuy $I, Scenario $scenario): void + public function generateAttributeForParameterAttributeWithMultipleParameters(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for parameter attribute with multiple parameters'); @@ -345,7 +336,7 @@ public function generateAttributeForParameterAttributeWithMultipleParameters(Cli $I->seeInThisFile("*/\n public function canSeeDirFound(#[\JetBrains\PhpStorm\Deprecated(\"it's old\", \"see a better method\")] \$dir) {"); } - public function generateAttributesForParameterAttributesWithMultipleAttributeDeclarations(CliGuy $I, Scenario $scenario): void + public function generateAttributesForParameterAttributesWithMultipleAttributeDeclarations(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for parameter attribute with multiple attribute declarations'); @@ -363,7 +354,7 @@ public function generateAttributesForParameterAttributesWithMultipleAttributeDec $I->seeInThisFile("*/\n public function canSeeDirFound(#[\JetBrains\PhpStorm\Deprecated(\"it's old\")]#[\JetBrains\PhpStorm\ExpectedValues([1, 2])] \$dir) {"); } - public function generateAttributesForParameterAttributesWithMultipleAttributesInASingleDeclaration(CliGuy $I, Scenario $scenario): void + public function generateAttributesForParameterAttributesWithMultipleAttributesInASingleDeclaration(CliGuy $I, Scenario $scenario) { $I->wantToTest('attribute generation for parameter attribute with multiple attributes in a single declaration'); diff --git a/tests/cli/DryRunCest.php b/tests/cli/DryRunCest.php index a7d6294aae..1e700b33cb 100644 --- a/tests/cli/DryRunCest.php +++ b/tests/cli/DryRunCest.php @@ -30,11 +30,7 @@ public function runFeature(CliGuy $I) public function runTestsWithTypedHelper(CliGuy $I) { - if (PHP_VERSION_ID < 80100) { - $I->markTestSkipped('Requires PHP 8.1'); - } - - $I->amInPath(\codecept_data_dir('typed_helper')); + $I->amInPath(codecept_data_dir('typed_helper')); $I->executeCommand('build'); $I->executeCommand('dry-run unit --no-ansi'); $I->seeInShellOutput('print comment'); diff --git a/tests/cli/IncludedCest.php b/tests/cli/IncludedCest.php index 9af16eb9c5..fb7fb343db 100644 --- a/tests/cli/IncludedCest.php +++ b/tests/cli/IncludedCest.php @@ -141,9 +141,9 @@ public function buildIncluded(CliGuy $I) { $I->executeCommand('build'); $I->seeInShellOutput('generated successfully'); - $I->seeInShellOutput('Jazz\\TestGuy'); - $I->seeInShellOutput('Jazz\\Pianist\\TestGuy'); - $I->seeInShellOutput('Shire\\TestGuy'); + $I->seeInShellOutput(\Jazz\TestGuy::class); + $I->seeInShellOutput(\Jazz\Pianist\TestGuy::class); + $I->seeInShellOutput(\Shire\TestGuy::class); } #[Before('moveToIncluded')] diff --git a/tests/cli/RunCest.php b/tests/cli/RunCest.php index a52bffdf32..0448d19ac5 100755 --- a/tests/cli/RunCest.php +++ b/tests/cli/RunCest.php @@ -417,9 +417,6 @@ public function runTestsWithSteps(CliGuy $I) ); } - /** - * @param CliGuy $I - */ public function runTestWithFailedScenario(CliGuy $I, $scenario) { if (!extension_loaded('xdebug')) { @@ -984,16 +981,10 @@ public function addTest(TestHtmlReportRegexBuilder $testBuilder): self class TestHtmlReportRegexBuilder { - private string $testClass; - - private string $testCase; - private $stepsRegex; - public function __construct(string $testClass, string $testCase) + public function __construct(private readonly string $testClass, private readonly string $testCase) { - $this->testClass = $testClass; - $this->testCase = $testCase; } public function getTestClass(): string diff --git a/tests/cli/RunSkippedCest.php b/tests/cli/RunSkippedCest.php index e3a5f4250e..7462262ed9 100644 --- a/tests/cli/RunSkippedCest.php +++ b/tests/cli/RunSkippedCest.php @@ -2,7 +2,7 @@ class RunSkippedCest { - public function classLevelSkipAnnotationWithMessage(CliGuy $I): void + public function classLevelSkipAnnotationWithMessage(CliGuy $I) { $I->amInPath('tests/data/skip'); $I->executeCommand('run -v --no-ansi unit ClassLevelSkipAnnotationWithMessageCest.php'); @@ -12,7 +12,7 @@ public function classLevelSkipAnnotationWithMessage(CliGuy $I): void $I->seeInShellOutput('OK, but incomplete, skipped, or useless tests!'); } - public function classLevelSkipAnnotationWithoutMessage(CliGuy $I): void + public function classLevelSkipAnnotationWithoutMessage(CliGuy $I) { $I->amInPath('tests/data/skip'); $I->executeCommand('run -v --no-ansi unit ClassLevelSkipAnnotationWithoutMessageCest.php'); @@ -21,7 +21,7 @@ public function classLevelSkipAnnotationWithoutMessage(CliGuy $I): void $I->seeInShellOutput('OK, but incomplete, skipped, or useless tests!'); } - public function classLevelSkipAttributeWithMessage(CliGuy $I): void + public function classLevelSkipAttributeWithMessage(CliGuy $I) { $I->amInPath('tests/data/skip'); $I->executeCommand('run -v --no-ansi unit ClassLevelSkipAttributeWithMessageCest.php'); @@ -31,7 +31,7 @@ public function classLevelSkipAttributeWithMessage(CliGuy $I): void $I->seeInShellOutput('OK, but incomplete, skipped, or useless tests!'); } - public function classLevelSkipAttributeWithoutMessage(CliGuy $I): void + public function classLevelSkipAttributeWithoutMessage(CliGuy $I) { $I->amInPath('tests/data/skip'); $I->executeCommand('run -v --no-ansi unit ClassLevelSkipAttributeWithoutMessageCest.php'); @@ -40,7 +40,7 @@ public function classLevelSkipAttributeWithoutMessage(CliGuy $I): void $I->seeInShellOutput('OK, but incomplete, skipped, or useless tests!'); } - public function methodLevelSkipAnnotationWithMessage(CliGuy $I): void + public function methodLevelSkipAnnotationWithMessage(CliGuy $I) { $I->amInPath('tests/data/skip'); $I->executeCommand('run -v --no-ansi unit MethodLevelSkipAnnotationWithMessageCest.php'); @@ -50,7 +50,7 @@ public function methodLevelSkipAnnotationWithMessage(CliGuy $I): void $I->seeInShellOutput('OK, but incomplete, skipped, or useless tests!'); } - public function methodLevelSkipAnnotationWithoutMessage(CliGuy $I): void + public function methodLevelSkipAnnotationWithoutMessage(CliGuy $I) { $I->amInPath('tests/data/skip'); $I->executeCommand('run -v --no-ansi unit MethodLevelSkipAnnotationWithoutMessageCest.php'); @@ -59,7 +59,7 @@ public function methodLevelSkipAnnotationWithoutMessage(CliGuy $I): void $I->seeInShellOutput('OK, but incomplete, skipped, or useless tests!'); } - public function methodLevelSkipAttributeWithMessage(CliGuy $I): void + public function methodLevelSkipAttributeWithMessage(CliGuy $I) { $I->amInPath('tests/data/skip'); $I->executeCommand('run -v --no-ansi unit MethodLevelSkipAttributeWithMessageCest.php'); @@ -69,7 +69,7 @@ public function methodLevelSkipAttributeWithMessage(CliGuy $I): void $I->seeInShellOutput('OK, but incomplete, skipped, or useless tests!'); } - public function methodLevelSkipAttributeWithoutMessage(CliGuy $I): void + public function methodLevelSkipAttributeWithoutMessage(CliGuy $I) { $I->amInPath('tests/data/skip'); $I->executeCommand('run -v --no-ansi unit MethodLevelSkipAttributeWithoutMessageCest.php'); diff --git a/tests/cli/RunUselessTestsCest.php b/tests/cli/RunUselessTestsCest.php index 266e706337..9793dc575e 100644 --- a/tests/cli/RunUselessTestsCest.php +++ b/tests/cli/RunUselessTestsCest.php @@ -2,7 +2,7 @@ class RunUselessTestsCest { - public function checkOutput(CliGuy $I): void + public function checkOutput(CliGuy $I) { $I->amInPath('tests/data/useless'); $I->executeCommand('run'); @@ -82,7 +82,7 @@ public function checkOutput(CliGuy $I): void ); } - public function checkReports(CliGuy $I): void + public function checkReports(CliGuy $I) { $I->amInPath('tests/data/useless'); $I->executeCommand('run --report --xml --phpunit-xml --html'); diff --git a/tests/cli/WantToCest.php b/tests/cli/WantToCest.php index d30747ac66..d587e40ffe 100644 --- a/tests/cli/WantToCest.php +++ b/tests/cli/WantToCest.php @@ -2,14 +2,14 @@ class WantToCest { - public function iWantToSetsFeatureInCeptFormat(CliGuy $I): void + public function iWantToSetsFeatureInCeptFormat(CliGuy $I) { $I->amInPath('tests/data/want_to'); $I->executeCommand('run --no-ansi unit WantToCept.php'); $I->seeInShellOutput('+ WantToCept: Check if wantTo works'); } - public function iWantToSetsFeatureInCestFormat(CliGuy $I): void + public function iWantToSetsFeatureInCestFormat(CliGuy $I) { $I->amInPath('tests/data/want_to'); $I->executeCommand('run --no-ansi unit WantToCest.php:^IWantTo'); @@ -19,14 +19,14 @@ public function iWantToSetsFeatureInCestFormat(CliGuy $I): void /** * Tests https://github.com/Codeception/Codeception/issues/4123 */ - public function testerWantDoesntSetFeatureInCestFormat(CliGuy $I): void + public function testerWantDoesntSetFeatureInCestFormat(CliGuy $I) { $I->amInPath('tests/data/want_to'); $I->executeCommand('run --no-ansi unit WantToCest.php:^TesterWantTo'); $I->seeInShellOutput('+ WantToCest: Tester want to'); } - public function iWantToWithVariableIsIgnored(CliGuy $I): void + public function iWantToWithVariableIsIgnored(CliGuy $I) { $I->amInPath('tests/data/want_to'); $I->executeCommand('run --no-ansi unit WantToCest.php:Variable'); @@ -36,7 +36,7 @@ public function iWantToWithVariableIsIgnored(CliGuy $I): void /** * Tests https://github.com/Codeception/Codeception/issues/4124 */ - public function iWantToDoesntOverrideDataproviderData(CliGuy $I): void + public function iWantToDoesntOverrideDataproviderData(CliGuy $I) { $I->amInPath('tests/data/want_to'); $I->executeCommand('run --no-ansi unit WantToCest.php:DataProviderIWantTo'); @@ -44,7 +44,7 @@ public function iWantToDoesntOverrideDataproviderData(CliGuy $I): void $I->seeInShellOutput('+ WantToCest: Check if I->wantTo doesn\\\'t override data provider data | "bbb"'); } - public function testerWantToDoesntOverrideDataproviderData(CliGuy $I): void + public function testerWantToDoesntOverrideDataproviderData(CliGuy $I) { $I->amInPath('tests/data/want_to'); $I->executeCommand('run --no-ansi unit WantToCest.php:DataProviderTesterWantTo'); @@ -52,7 +52,7 @@ public function testerWantToDoesntOverrideDataproviderData(CliGuy $I): void $I->seeInShellOutput('+ WantToCest: Data provider tester want to | "bbb"'); } - public function wantToTextIsUsedInXmlReport(CliGuy $I): void + public function wantToTextIsUsedInXmlReport(CliGuy $I) { $I->amInPath('tests/data/want_to'); $I->executeCommand('run unit --xml'); diff --git a/tests/unit/Codeception/ApplicationTest.php b/tests/unit/Codeception/ApplicationTest.php index fd3263a788..6f6b0b98b3 100644 --- a/tests/unit/Codeception/ApplicationTest.php +++ b/tests/unit/Codeception/ApplicationTest.php @@ -12,7 +12,7 @@ public function testRegisterCustomCommand() { \Codeception\Configuration::append(['extensions' => [ 'commands' => [ - 'Project\Command\MyCustomCommand']]]); + \Project\Command\MyCustomCommand::class]]]); $application = new Application(); $application->registerCustomCommands(); diff --git a/tests/unit/Codeception/Command/BaseCommandRunner.php b/tests/unit/Codeception/Command/BaseCommandRunner.php index 6bf27e83fb..6633792130 100644 --- a/tests/unit/Codeception/Command/BaseCommandRunner.php +++ b/tests/unit/Codeception/Command/BaseCommandRunner.php @@ -46,7 +46,7 @@ protected function execute(array $args = [], $isSuite = true) protected function makeCommand($className, $saved = true, $extraMethods = []) { - if (!$this->config) { + if ($this->config === []) { $this->config = []; } diff --git a/tests/unit/Codeception/Command/BuildTest.php b/tests/unit/Codeception/Command/BuildTest.php index 3416bdf2c6..89b3b45145 100644 --- a/tests/unit/Codeception/Command/BuildTest.php +++ b/tests/unit/Codeception/Command/BuildTest.php @@ -4,9 +4,6 @@ class BuildTest extends BaseCommandRunner { - /** - * @var array - */ public array $log = []; protected function _setUp() diff --git a/tests/unit/Codeception/Command/GenerateGroupTest.php b/tests/unit/Codeception/Command/GenerateGroupTest.php index c44a8c2f28..93ec205c8c 100644 --- a/tests/unit/Codeception/Command/GenerateGroupTest.php +++ b/tests/unit/Codeception/Command/GenerateGroupTest.php @@ -4,9 +4,6 @@ class GenerateGroupTest extends BaseCommandRunner { - /** - * @var array - */ public array $log = []; protected function _setUp() diff --git a/tests/unit/Codeception/Command/GenerateScenarioTest.php b/tests/unit/Codeception/Command/GenerateScenarioTest.php index 4078f352c0..b3f81c1eb0 100644 --- a/tests/unit/Codeception/Command/GenerateScenarioTest.php +++ b/tests/unit/Codeception/Command/GenerateScenarioTest.php @@ -7,19 +7,10 @@ class GenerateScenarioTest extends BaseCommandRunner { - /** - * @var array - */ public array $modules = []; - /** - * @var array - */ public array $actions = []; - /** - * @var ModuleContainer - */ protected ModuleContainer $moduleContainer; protected function _setUp() diff --git a/tests/unit/Codeception/Command/GenerateStepObjectTest.php b/tests/unit/Codeception/Command/GenerateStepObjectTest.php index 684841cded..cd98a5a91f 100644 --- a/tests/unit/Codeception/Command/GenerateStepObjectTest.php +++ b/tests/unit/Codeception/Command/GenerateStepObjectTest.php @@ -4,9 +4,6 @@ class GenerateStepObjectTest extends BaseCommandRunner { - /** - * @var array - */ public array $log = []; protected function _setUp() diff --git a/tests/unit/Codeception/Command/GenerateSuiteTest.php b/tests/unit/Codeception/Command/GenerateSuiteTest.php index e241285a09..5e0f098a6f 100644 --- a/tests/unit/Codeception/Command/GenerateSuiteTest.php +++ b/tests/unit/Codeception/Command/GenerateSuiteTest.php @@ -4,9 +4,6 @@ class GenerateSuiteTest extends BaseCommandRunner { - /** - * @var array - */ public array $log = []; /** diff --git a/tests/unit/Codeception/Command/GenerateTestTest.php b/tests/unit/Codeception/Command/GenerateTestTest.php index fb3dc5586c..48a667bd1e 100644 --- a/tests/unit/Codeception/Command/GenerateTestTest.php +++ b/tests/unit/Codeception/Command/GenerateTestTest.php @@ -52,7 +52,7 @@ public function testGenerateWithSupportNamespaced() $this->config['namespace'] = 'MiddleEarth'; $this->config['support_namespace'] = 'Gondor'; $this->execute(['suite' => 'shire', 'class' => 'HallUnderTheHill']); - $this->assertEquals($this->filename, 'tests/shire/HallUnderTheHillTest.php'); + $this->assertSame($this->filename, 'tests/shire/HallUnderTheHillTest.php'); $this->assertStringContainsString('namespace MiddleEarth\Unit;', $this->content); $this->assertStringContainsString('use MiddleEarth\\Gondor\\HobbitGuy;', $this->content); $this->assertIsValidPhp($this->content); diff --git a/tests/unit/Codeception/Coverage/FilterTest.php b/tests/unit/Codeception/Coverage/FilterTest.php index 7f5031d182..3af8514bd1 100644 --- a/tests/unit/Codeception/Coverage/FilterTest.php +++ b/tests/unit/Codeception/Coverage/FilterTest.php @@ -39,13 +39,12 @@ public function testWhitelistFilterApplied() ]; $this->filter->whiteList($config); $fileFilter = $this->filter->getFilter(); - $filterMethod = $this->getFilterMethod(); - $this->assertFalse($fileFilter->$filterMethod(codecept_root_dir('tests/unit/C3Test.php'))); - $this->assertFalse($fileFilter->$filterMethod(codecept_root_dir('src/Codeception/Codecept.php'))); - $this->assertTrue($fileFilter->$filterMethod(codecept_root_dir('vendor/guzzlehttp/guzzle/src/Client.php'))); - $this->assertTrue($fileFilter->$filterMethod(codecept_root_dir('tests/support/CodeGuy.php'))); + $this->assertFalse($fileFilter->isExcluded(codecept_root_dir('tests/unit/C3Test.php'))); + $this->assertFalse($fileFilter->isExcluded(codecept_root_dir('src/Codeception/Codecept.php'))); + $this->assertTrue($fileFilter->isExcluded(codecept_root_dir('vendor/guzzlehttp/guzzle/src/Client.php'))); + $this->assertTrue($fileFilter->isExcluded(codecept_root_dir('tests/support/CodeGuy.php'))); $this->assertTrue( - $fileFilter->$filterMethod( + $fileFilter->isExcluded( codecept_root_dir('tests/unit.suite.yml') ), 'tests/unit.suite.yml appears in file list' @@ -60,9 +59,8 @@ public function testShortcutFilter() ]]; $this->filter->whiteList($config); $fileFilter = $this->filter->getFilter(); - $filterMethod = $this->getFilterMethod(); - $this->assertFalse($fileFilter->$filterMethod(codecept_root_dir('tests/unit/C3Test.php'))); - $this->assertTrue($fileFilter->$filterMethod(codecept_root_dir('tests/support/CodeGuy.php'))); + $this->assertFalse($fileFilter->isExcluded(codecept_root_dir('tests/unit/C3Test.php'))); + $this->assertTrue($fileFilter->isExcluded(codecept_root_dir('tests/support/CodeGuy.php'))); } public function testWhitelistIncludeFilterApplied() @@ -80,27 +78,15 @@ public function testWhitelistIncludeFilterApplied() ]; $this->filter->whiteList($config); $fileFilter = $this->filter->getFilter(); - $filterMethod = $this->getFilterMethod(); - $this->assertFalse($fileFilter->$filterMethod(codecept_root_dir('tests/unit/C3Test.php'))); - $this->assertFalse($fileFilter->$filterMethod(codecept_root_dir('src/Codeception/Codecept.php'))); - $this->assertFalse($fileFilter->$filterMethod(codecept_root_dir('tests/support/CodeGuy.php'))); - $this->assertTrue($fileFilter->$filterMethod(codecept_root_dir('vendor/guzzlehttp/guzzle/src/Client.php'))); + $this->assertFalse($fileFilter->isExcluded(codecept_root_dir('tests/unit/C3Test.php'))); + $this->assertFalse($fileFilter->isExcluded(codecept_root_dir('src/Codeception/Codecept.php'))); + $this->assertFalse($fileFilter->isExcluded(codecept_root_dir('tests/support/CodeGuy.php'))); + $this->assertTrue($fileFilter->isExcluded(codecept_root_dir('vendor/guzzlehttp/guzzle/src/Client.php'))); $this->assertTrue( - $fileFilter->$filterMethod( + $fileFilter->isExcluded( codecept_root_dir('tests/unit.suite.yml') ), 'tests/unit.suite.yml appears in file list' ); } - - private function getFilterMethod(): string - { - $filterMethod = 'isFiltered'; - if (method_exists($this->filter->getFilter(), 'isExcluded')) { - //php-code-coverage 9+ - $filterMethod = 'isExcluded'; - } - - return $filterMethod; - } } diff --git a/tests/unit/Codeception/Lib/Console/DiffFactoryTest.php b/tests/unit/Codeception/Lib/Console/DiffFactoryTest.php index 445f033929..2d26c621b2 100644 --- a/tests/unit/Codeception/Lib/Console/DiffFactoryTest.php +++ b/tests/unit/Codeception/Lib/Console/DiffFactoryTest.php @@ -11,9 +11,6 @@ **/ class DiffFactoryTest extends \Codeception\Test\Unit { - /** - * @var DiffFactory - */ protected DiffFactory $diffFactory; protected function _setUp() diff --git a/tests/unit/Codeception/Lib/DiTest.php b/tests/unit/Codeception/Lib/DiTest.php index 0ad1722a64..7084e2f693 100644 --- a/tests/unit/Codeception/Lib/DiTest.php +++ b/tests/unit/Codeception/Lib/DiTest.php @@ -8,9 +8,6 @@ class DiTest extends \Codeception\Test\Unit { - /** - * @var Di - */ protected Di $di; protected function _setUp() @@ -31,13 +28,13 @@ public function testFailDependenciesCyclic() $this->injectionShouldFail( 'Failed to resolve cyclic dependencies for class \'FailDependenciesCyclic\IncorrectDependenciesClass\'' ); - $this->di->instantiate('FailDependenciesCyclic\IncorrectDependenciesClass'); + $this->di->instantiate(\FailDependenciesCyclic\IncorrectDependenciesClass::class); } public function testFailDependenciesInChain() { $this->injectionShouldFail('Failed to resolve dependency \'FailDependenciesInChain\AnotherClass\''); - $this->di->instantiate('FailDependenciesInChain\IncorrectDependenciesClass'); + $this->di->instantiate(\FailDependenciesInChain\IncorrectDependenciesClass::class); } public function testFailDependenciesNonExistent() @@ -45,12 +42,12 @@ public function testFailDependenciesNonExistent() $expectedExceptionMessage = 'Class "FailDependenciesNonExistent\NonExistentClass" does not exist'; $this->injectionShouldFail($expectedExceptionMessage); - $this->di->instantiate('FailDependenciesNonExistent\IncorrectDependenciesClass'); + $this->di->instantiate(\FailDependenciesNonExistent\IncorrectDependenciesClass::class); } public function testFailDependenciesPrimitiveParam() { $this->injectionShouldFail("Parameter 'required' must have default value"); - $this->di->instantiate('FailDependenciesPrimitiveParam\IncorrectDependenciesClass'); + $this->di->instantiate(\FailDependenciesPrimitiveParam\IncorrectDependenciesClass::class); } } diff --git a/tests/unit/Codeception/Lib/ModuleContainerTest.php b/tests/unit/Codeception/Lib/ModuleContainerTest.php index 22f79abbaa..67954398b0 100644 --- a/tests/unit/Codeception/Lib/ModuleContainerTest.php +++ b/tests/unit/Codeception/Lib/ModuleContainerTest.php @@ -35,14 +35,14 @@ protected function _tearDown() public function testCreateModule() { $module = $this->moduleContainer->create('EmulateModuleHelper'); - $this->assertInstanceOf('Codeception\Module\EmulateModuleHelper', $module); + $this->assertInstanceOf(\Codeception\Module\EmulateModuleHelper::class, $module); - $module = $this->moduleContainer->create('Codeception\Module\EmulateModuleHelper'); - $this->assertInstanceOf('Codeception\Module\EmulateModuleHelper', $module); + $module = $this->moduleContainer->create(\Codeception\Module\EmulateModuleHelper::class); + $this->assertInstanceOf(\Codeception\Module\EmulateModuleHelper::class, $module); $this->assertTrue($this->moduleContainer->hasModule('EmulateModuleHelper')); $this->assertInstanceOf( - 'Codeception\Module\EmulateModuleHelper', + \Codeception\Module\EmulateModuleHelper::class, $this->moduleContainer->getModule('EmulateModuleHelper') ); } @@ -104,7 +104,7 @@ public function testActionsExplicitlySetForNotInheritedModule() public function testCreateModuleWithoutRequiredFields() { $this->expectException(\Codeception\Exception\ModuleConfigException::class); - $this->moduleContainer->create('Codeception\Lib\StubModule'); + $this->moduleContainer->create(\Codeception\Lib\StubModule::class); } #[Group('core')] @@ -113,7 +113,7 @@ public function testCreateModuleWithCorrectConfig() $config = [ 'modules' => [ 'config' => [ - 'Codeception\Lib\StubModule' => [ + \Codeception\Lib\StubModule::class => [ 'firstField' => 'firstValue', 'secondField' => 'secondValue', ] @@ -122,7 +122,7 @@ public function testCreateModuleWithCorrectConfig() ]; $this->moduleContainer = new ModuleContainer(Stub::make(\Codeception\Lib\Di::class), $config); - $module = $this->moduleContainer->create('Codeception\Lib\StubModule'); + $module = $this->moduleContainer->create(\Codeception\Lib\StubModule::class); $this->assertSame('firstValue', $module->_getFirstField()); $this->assertSame('secondValue', $module->_getSecondField()); @@ -134,7 +134,7 @@ public function testReconfigureModule() $config = [ 'modules' => [ 'config' => [ - 'Codeception\Lib\StubModule' => [ + \Codeception\Lib\StubModule::class => [ 'firstField' => 'firstValue', 'secondField' => 'secondValue', ] @@ -142,7 +142,7 @@ public function testReconfigureModule() ] ]; $this->moduleContainer = new ModuleContainer(Stub::make(\Codeception\Lib\Di::class), $config); - $module = $this->moduleContainer->create('Codeception\Lib\StubModule'); + $module = $this->moduleContainer->create(\Codeception\Lib\StubModule::class); $module->_reconfigure(['firstField' => '1st', 'secondField' => '2nd']); $this->assertSame('1st', $module->_getFirstField()); $this->assertSame('2nd', $module->_getSecondField()); @@ -154,7 +154,7 @@ public function testReconfigureModule() public function testConflictsByModuleName() { $this->expectException(\Codeception\Exception\ModuleConflictException::class); - $this->moduleContainer->create('Codeception\Lib\ConflictedModule'); + $this->moduleContainer->create(\Codeception\Lib\ConflictedModule::class); $this->moduleContainer->create('Cli'); $this->moduleContainer->validateConflicts(); } @@ -163,7 +163,7 @@ public function testConflictsByModuleName() public function testConflictsByClass() { $this->expectException(\Codeception\Exception\ModuleConflictException::class); - $this->moduleContainer->create('Codeception\Lib\ConflictedModule2'); + $this->moduleContainer->create(\Codeception\Lib\ConflictedModule2::class); $this->moduleContainer->create('Cli'); $this->moduleContainer->validateConflicts(); } @@ -171,7 +171,7 @@ public function testConflictsByClass() public function testModuleDependenciesFail() { $this->expectException(\Codeception\Exception\ModuleRequireException::class); - $this->moduleContainer->create('Codeception\Lib\DependencyModule'); + $this->moduleContainer->create(\Codeception\Lib\DependencyModule::class); } public function testModuleDependencies() @@ -192,16 +192,16 @@ public function testModuleDependencies() public function testModuleParts1() { $config = ['modules' => [ - 'enabled' => ['\Codeception\Lib\PartedModule'], + 'enabled' => [\Codeception\Lib\PartedModule::class], 'config' => [ - '\Codeception\Lib\PartedModule' => [ + \Codeception\Lib\PartedModule::class => [ 'part' => 'one' ] ] ] ]; $this->moduleContainer = new ModuleContainer(Stub::make(\Codeception\Lib\Di::class), $config); - $this->moduleContainer->create('\Codeception\Lib\PartedModule'); + $this->moduleContainer->create(\Codeception\Lib\PartedModule::class); $actions = $this->moduleContainer->getActions(); $this->assertArrayHasKey('partOne', $actions); @@ -211,15 +211,15 @@ public function testModuleParts1() public function testModuleParts2() { $config = ['modules' => [ - 'enabled' => ['\Codeception\Lib\PartedModule'], - 'config' => ['\Codeception\Lib\PartedModule' => [ + 'enabled' => [\Codeception\Lib\PartedModule::class], + 'config' => [\Codeception\Lib\PartedModule::class => [ 'part' => ['Two'] ] ] ] ]; $this->moduleContainer = new ModuleContainer(Stub::make(\Codeception\Lib\Di::class), $config); - $this->moduleContainer->create('\Codeception\Lib\PartedModule'); + $this->moduleContainer->create(\Codeception\Lib\PartedModule::class); $actions = $this->moduleContainer->getActions(); $this->assertArrayHasKey('partTwo', $actions); @@ -232,7 +232,7 @@ public function testShortConfigParts() 'modules' => [ 'enabled' => [ [ - '\Codeception\Lib\PartedModule' => [ + \Codeception\Lib\PartedModule::class => [ 'part' => 'one' ], ], @@ -240,7 +240,7 @@ public function testShortConfigParts() ], ]; $this->moduleContainer = new ModuleContainer(Stub::make(\Codeception\Lib\Di::class), $config); - $this->moduleContainer->create('\Codeception\Lib\PartedModule'); + $this->moduleContainer->create(\Codeception\Lib\PartedModule::class); $actions = $this->moduleContainer->getActions(); $this->assertArrayHasKey('partOne', $actions); @@ -252,7 +252,7 @@ public function testShortConfigFormat() $config = [ 'modules' => ['enabled' => [ - ['Codeception\Lib\StubModule' => [ + [\Codeception\Lib\StubModule::class => [ 'firstField' => 'firstValue', 'secondField' => 'secondValue', ] @@ -262,7 +262,7 @@ public function testShortConfigFormat() ]; $this->moduleContainer = new ModuleContainer(Stub::make(\Codeception\Lib\Di::class), $config); - $module = $this->moduleContainer->create('Codeception\Lib\StubModule'); + $module = $this->moduleContainer->create(\Codeception\Lib\StubModule::class); $this->assertSame('firstValue', $module->_getFirstField()); $this->assertSame('secondValue', $module->_getSecondField()); @@ -283,23 +283,23 @@ public function testShortConfigDependencies() public function testInjectModuleIntoHelper() { $config = ['modules' => [ - 'enabled' => ['Codeception\Lib\HelperModule'], + 'enabled' => [\Codeception\Lib\HelperModule::class], ]]; $this->moduleContainer = new ModuleContainer(Stub::make(\Codeception\Lib\Di::class), $config); - $this->moduleContainer->create('Codeception\Lib\HelperModule'); - $this->assertTrue($this->moduleContainer->hasModule('Codeception\Lib\HelperModule')); + $this->moduleContainer->create(\Codeception\Lib\HelperModule::class); + $this->assertTrue($this->moduleContainer->hasModule(\Codeception\Lib\HelperModule::class)); } public function testSuggestMissingModule() { - $correctModule = 'Codeception\Lib\HelperModule'; + $correctModule = \Codeception\Lib\HelperModule::class; $wrongModule = 'Codeception\Lib\Helpamodule'; $config = ['modules' => [ 'enabled' => [$correctModule], ]]; $this->moduleContainer = new ModuleContainer(Stub::make(\Codeception\Lib\Di::class), $config); - $this->moduleContainer->create('Codeception\Lib\HelperModule'); + $this->moduleContainer->create(\Codeception\Lib\HelperModule::class); $message = "Codeception\Lib\ModuleContainer: Module {$wrongModule} couldn't be connected (did you mean '{$correctModule}'?)"; $this->expectException(\Codeception\Exception\ModuleException::class); @@ -366,7 +366,7 @@ class DependencyModule extends Module implements DependsOnModule { public function _depends(): array { - return ['Codeception\Lib\ConflictedModule' => 'Error message']; + return [\Codeception\Lib\ConflictedModule::class => 'Error message']; } public function _inject() diff --git a/tests/unit/Codeception/Lib/ParserTest.php b/tests/unit/Codeception/Lib/ParserTest.php index 4e86a9a6f5..242a6de998 100644 --- a/tests/unit/Codeception/Lib/ParserTest.php +++ b/tests/unit/Codeception/Lib/ParserTest.php @@ -167,13 +167,13 @@ public function testClassesFromFile() public function testNamedParameterNamedClassIsNotClass() { $classes = Parser::getClassesFromFile(codecept_data_dir('namedParameter.php')); - $this->assertEquals([], $classes); + $this->assertSame([], $classes); } #[Group('core')] public function testParseTestContainingAnnonymousClassWithAttribute() { $classes = Parser::getClassesFromFile(codecept_data_dir('AnonymousClassWithAttributeCest.php')); - $this->assertEquals(['Tests\Unit\AnonymousClassWithAttributeCest'], $classes); + $this->assertSame([\Tests\Unit\AnonymousClassWithAttributeCest::class], $classes); } } diff --git a/tests/unit/Codeception/ScenarioTest.php b/tests/unit/Codeception/ScenarioTest.php index d6ac401816..c9324bcfe1 100644 --- a/tests/unit/Codeception/ScenarioTest.php +++ b/tests/unit/Codeception/ScenarioTest.php @@ -6,7 +6,7 @@ class ScenarioTest extends \PHPUnit\Framework\TestCase { public function testGetHtml() { - $step1 = $this->getMockBuilder('\Codeception\Step') + $step1 = $this->getMockBuilder(\Codeception\Step::class) ->setConstructorArgs([ 'Do some testing', [ @@ -16,7 +16,7 @@ public function testGetHtml() ]) ->onlyMethods([]) ->getMock(); - $step2 = $this->getMockBuilder('\Codeception\Step') + $step2 = $this->getMockBuilder(\Codeception\Step::class) ->setConstructorArgs([ 'Do even more testing without args', [] diff --git a/tests/unit/Codeception/Step/RetryTest.php b/tests/unit/Codeception/Step/RetryTest.php index 276a3d5379..b3dbf30cc1 100644 --- a/tests/unit/Codeception/Step/RetryTest.php +++ b/tests/unit/Codeception/Step/RetryTest.php @@ -9,9 +9,6 @@ class RetryTest extends \PHPUnit\Framework\TestCase { - /** - * @var bool - */ protected bool $shouldFail = true; public function testRetryStepShouldNotFailStep() diff --git a/tests/unit/Codeception/Step/TryToTest.php b/tests/unit/Codeception/Step/TryToTest.php index 1aff183747..66be6b8d12 100644 --- a/tests/unit/Codeception/Step/TryToTest.php +++ b/tests/unit/Codeception/Step/TryToTest.php @@ -9,9 +9,6 @@ class TryToTest extends \PHPUnit\Framework\TestCase { - /** - * @var bool - */ protected bool $shouldFail = true; public function testTryToShouldReturnSuccess() @@ -39,7 +36,7 @@ public function testTryStepShouldNotFailStep() $this->assertFalse($tryTo->hasFailed(), 'successful retry still marks test as failed'); } - public function _executeFailedCode() + public function _executeFailedCode(): never { throw new \Exception('Error'); } diff --git a/tests/unit/Codeception/StepTest.php b/tests/unit/Codeception/StepTest.php index 76fdb5914f..904cdb8f24 100644 --- a/tests/unit/Codeception/StepTest.php +++ b/tests/unit/Codeception/StepTest.php @@ -13,7 +13,7 @@ class StepTest extends TestCase { protected function getStep(array $args): Step { - return $this->getMockBuilder('\Codeception\Step') + return $this->getMockBuilder(\Codeception\Step::class) ->setConstructorArgs($args) ->onlyMethods([]) ->getMock(); @@ -141,7 +141,7 @@ public function testFormattedOutput() $this->assertSame('argument "some formatted output"', $output); } - public function testConstraintOutput(): void + public function testConstraintOutput() { $argument = Stub::makeEmpty(Constraint::class); $argument->method('toString')->willReturn('is a constraint'); diff --git a/tests/unit/Codeception/Test/DataProviderTest.php b/tests/unit/Codeception/Test/DataProviderTest.php index a7b289e24b..661df89b92 100644 --- a/tests/unit/Codeception/Test/DataProviderTest.php +++ b/tests/unit/Codeception/Test/DataProviderTest.php @@ -11,38 +11,38 @@ class DataProviderTest extends Unit { protected \CodeGuy $tester; - public function testParsesAnnotationContainingMethodNameOnly(): void + public function testParsesAnnotationContainingMethodNameOnly() { $result = DataProvider::parseDataProviderAnnotation('getData', 'UnitTest', 'testMethod'); - self::assertSame(['UnitTest', 'getData'], $result); + $this->assertSame(['UnitTest', 'getData'], $result); } - public function testParsesAnnotationContainingClassNameAndMethodName(): void + public function testParsesAnnotationContainingClassNameAndMethodName() { $result = DataProvider::parseDataProviderAnnotation('AnotherClass::getData', 'UnitTest', 'testMethod'); - self::assertSame(['AnotherClass', 'getData'], $result); + $this->assertSame(['AnotherClass', 'getData'], $result); } - public function testParsesAnnotationContainingNamespacedClassNameAndMethodName(): void + public function testParsesAnnotationContainingNamespacedClassNameAndMethodName() { $result = DataProvider::parseDataProviderAnnotation('Namespace\AnotherClass::getData', 'UnitTest', 'testMethod'); - self::assertSame(['Namespace\AnotherClass', 'getData'], $result); + $this->assertSame(['Namespace\AnotherClass', 'getData'], $result); } - public function testParseAnnotationThrowsExceptionIfAnnotationContainsTooManyDoubleColons(): void + public function testParseAnnotationThrowsExceptionIfAnnotationContainsTooManyDoubleColons() { $this->expectException(InvalidTestException::class); $this->expectExceptionMessage('Data provider "AnotherClass::bug::getData" specified for UnitTest::testMethod is invalid'); - $result = DataProvider::parseDataProviderAnnotation('AnotherClass::bug::getData', 'UnitTest', 'testMethod'); + DataProvider::parseDataProviderAnnotation('AnotherClass::bug::getData', 'UnitTest', 'testMethod'); } - public function testReturnsNullIfMethodHasNoDataProvider(): void + public function testReturnsNullIfMethodHasNoDataProvider() { $method = new ReflectionMethod($this, __FUNCTION__); - self::assertNull(DataProvider::getDataForMethod($method)); + $this->assertNull(DataProvider::getDataForMethod($method)); } - public function testExecutesPublicStaticDataProviderInTheSameClass(): void + public function testExecutesPublicStaticDataProviderInTheSameClass() { require_once codecept_data_dir('data_provider/PublicStaticDataProviderTest.php'); $method = new ReflectionMethod(PublicStaticDataProviderTest::class, 'testDataProvider'); @@ -54,10 +54,10 @@ public function testExecutesPublicStaticDataProviderInTheSameClass(): void 'not baz' => ['baz', 5], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testExecutesPublicDataProviderInTheSameClass(): void + public function testExecutesPublicDataProviderInTheSameClass() { require_once codecept_data_dir('data_provider/PublicDataProviderTest.php'); $method = new ReflectionMethod(PublicDataProviderTest::class, 'testDataProvider'); @@ -69,10 +69,10 @@ public function testExecutesPublicDataProviderInTheSameClass(): void 'not baz' => ['baz', 7], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testExecutesPrivateDataProviderInTheSameClass(): void + public function testExecutesPrivateDataProviderInTheSameClass() { require_once codecept_data_dir('data_provider/PrivateDataProviderTest.php'); $method = new ReflectionMethod(PrivateDataProviderTest::class, 'testDataProvider'); @@ -84,10 +84,10 @@ public function testExecutesPrivateDataProviderInTheSameClass(): void ['baz', 7], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testExecutesDataProviderSpecifiedUsingAttribute(): void + public function testExecutesDataProviderSpecifiedUsingAttribute() { require_once codecept_data_dir('data_provider/AttributeDataProviderTest.php'); $method = new ReflectionMethod(AttributeDataProviderTest::class, 'testDataProvider'); @@ -99,12 +99,12 @@ public function testExecutesDataProviderSpecifiedUsingAttribute(): void 'not baz' => ['baz', 7], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testExecutesPrivateDataProviderInAnotherClass(): void + public function testExecutesPrivateDataProviderInAnotherClass() { require_once codecept_data_dir('data_provider/DataProviderInAnotherClassTest.php'); $method = new ReflectionMethod(DataProviderInAnotherClassTest::class, 'testDataProvider'); @@ -116,10 +116,10 @@ public function testExecutesPrivateDataProviderInAnotherClass(): void 'not baz' => ['baz', 7], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testExecutesMultipleDataProviders(): void + public function testExecutesMultipleDataProviders() { require_once codecept_data_dir('data_provider/MultipleDataProviderTest.php'); $method = new ReflectionMethod(MultipleDataProviderTest::class, 'testDataProvider'); @@ -133,10 +133,10 @@ public function testExecutesMultipleDataProviders(): void 'def' => ['def', 9], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testSupportsExampleAnnotations(): void + public function testSupportsExampleAnnotations() { require_once codecept_data_dir('data_provider/ExampleAnnotationTest.php'); $method = new ReflectionMethod(ExampleAnnotationTest::class, 'testExample'); @@ -147,10 +147,10 @@ public function testSupportsExampleAnnotations(): void ['bar', 6], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testSupportsExamplesAttribute(): void + public function testSupportsExamplesAttribute() { require_once codecept_data_dir('data_provider/ExamplesAttributeTest.php'); $method = new ReflectionMethod(ExamplesAttributeTest::class, 'testExample'); @@ -161,10 +161,10 @@ public function testSupportsExamplesAttribute(): void ['bar', 8], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testCombinesExampleAndDataProviderAnnotations(): void + public function testCombinesExampleAndDataProviderAnnotations() { require_once codecept_data_dir('data_provider/CombinedAnnotationDataProviderTest.php'); $method = new ReflectionMethod(CombinedAnnotationDataProviderTest::class, 'testCombined'); @@ -177,10 +177,10 @@ public function testCombinesExampleAndDataProviderAnnotations(): void 'def' => ['def', 9], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testCombinesExampleAndDataProviderAttributes(): void + public function testCombinesExampleAndDataProviderAttributes() { require_once codecept_data_dir('data_provider/CombinedAttributeDataProviderTest.php'); $method = new ReflectionMethod(CombinedAttributeDataProviderTest::class, 'testCombined'); @@ -194,10 +194,10 @@ public function testCombinesExampleAndDataProviderAttributes(): void 'not baz' => ['baz', 7], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testExecutesPublicDataProviderInAnotherAbstractClass(): void + public function testExecutesPublicDataProviderInAnotherAbstractClass() { require_once codecept_data_dir('data_provider/AbstractDataProviderTest.php'); $method = new ReflectionMethod(AbstractDataProviderTest::class, 'testDataProvider'); @@ -207,10 +207,10 @@ public function testExecutesPublicDataProviderInAnotherAbstractClass(): void 'foo' => ['foo'], ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } - public function testDataProviderReceivesActor(): void + public function testDataProviderReceivesActor() { require_once codecept_data_dir('data_provider/DataProviderReceivesActorTest.php'); $method = new ReflectionMethod(DataProviderReceivesActorTest::class, 'testDataProvider'); @@ -220,6 +220,6 @@ public function testDataProviderReceivesActor(): void 'codeGuyMethod() exists' ]; - self::assertSame($expectedResult, $result); + $this->assertSame($expectedResult, $result); } } diff --git a/tests/unit/Codeception/Util/MockAutoload.php b/tests/unit/Codeception/Util/MockAutoload.php index 62416d8ce2..cbb0ad65f4 100644 --- a/tests/unit/Codeception/Util/MockAutoload.php +++ b/tests/unit/Codeception/Util/MockAutoload.php @@ -6,9 +6,6 @@ class MockAutoload extends Autoload { - /** - * @var array - */ protected static array $files = []; /** diff --git a/tests/unit/Codeception/Util/ReflectionHelperTest.php b/tests/unit/Codeception/Util/ReflectionHelperTest.php index 47940fd93d..b293e7cdca 100644 --- a/tests/unit/Codeception/Util/ReflectionHelperTest.php +++ b/tests/unit/Codeception/Util/ReflectionHelperTest.php @@ -79,19 +79,19 @@ public function testGetClassFromParameter() $this->assertSame( \Codeception\Util\Debug::class, - ReflectionHelper::getClassFromParameter(new ReflectionParameter([$object, 'setDebug'], 0)) + ReflectionHelper::getClassFromParameter(new ReflectionParameter($object->setDebug(...), 0)) ); $this->assertNull( - ReflectionHelper::getClassFromParameter(new ReflectionParameter([$object, 'setDebug'], 1)) + ReflectionHelper::getClassFromParameter(new ReflectionParameter($object->setDebug(...), 1)) ); $this->assertNull( - ReflectionHelper::getClassFromParameter(new ReflectionParameter([$object, 'setDebug'], 'flavor')) + ReflectionHelper::getClassFromParameter(new ReflectionParameter($object->setDebug(...), 'flavor')) ); $this->assertNull( - ReflectionHelper::getClassFromParameter(new ReflectionParameter([$object, 'setInt'], 'i')) + ReflectionHelper::getClassFromParameter(new ReflectionParameter($object->setInt(...), 'i')) ); } @@ -102,37 +102,37 @@ public function testGetDefaultValue() $this->assertSame( 'null', - ReflectionHelper::getDefaultValue(new ReflectionParameter([$object, 'setDebug'], 0)) + ReflectionHelper::getDefaultValue(new ReflectionParameter($object->setDebug(...), 0)) ); $this->assertSame( '\Codeception\Util\ReflectionTestClass::FOO', - ReflectionHelper::getDefaultValue(new ReflectionParameter([$object, 'setDebug'], 1)) + ReflectionHelper::getDefaultValue(new ReflectionParameter($object->setDebug(...), 1)) ); $this->assertSame( '\Codeception\Util\ReflectionTestClass::FOO', - ReflectionHelper::getDefaultValue(new ReflectionParameter([$object, 'setDebug'], 'flavor')) + ReflectionHelper::getDefaultValue(new ReflectionParameter($object->setDebug(...), 'flavor')) ); $this->assertSame( '\Codeception\Codecept::VERSION', - ReflectionHelper::getDefaultValue(new ReflectionParameter([$object, 'setFlavorImportedDefault'], 'flavor')) + ReflectionHelper::getDefaultValue(new ReflectionParameter($object->setFlavorImportedDefault(...), 'flavor')) ); $this->assertSame( "''", - ReflectionHelper::getDefaultValue(new ReflectionParameter([$object, 'setValue'], 0)) + ReflectionHelper::getDefaultValue(new ReflectionParameter($object->setValue(...), 0)) ); $this->assertSame( '0', - ReflectionHelper::getDefaultValue(new ReflectionParameter([$object, 'setInt'], 0)) + ReflectionHelper::getDefaultValue(new ReflectionParameter($object->setInt(...), 0)) ); $this->assertSame( 'null', - ReflectionHelper::getDefaultValue(new ReflectionParameter([$object, 'setMixed'], 0)) + ReflectionHelper::getDefaultValue(new ReflectionParameter($object->setMixed(...), 0)) ); } From 9909ac7bf747618abb60cca6add0aeb2dec30930 Mon Sep 17 00:00:00 2001 From: Aaron Gustavo Nieves <64917965+TavoNiievez@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:26:44 -0500 Subject: [PATCH 05/25] Simplify Coverage classes (#6764) --- src/Codeception/Coverage/Filter.php | 70 ++++------ .../Coverage/PhpCodeCoverageFactory.php | 14 +- src/Codeception/Coverage/Subscriber/Local.php | 5 - .../Coverage/Subscriber/LocalServer.php | 124 +++++++----------- .../Coverage/Subscriber/Printer.php | 121 ++++++----------- .../Coverage/Subscriber/RemoteServer.php | 54 ++------ src/Codeception/Coverage/SuiteSubscriber.php | 5 + 7 files changed, 137 insertions(+), 256 deletions(-) diff --git a/src/Codeception/Coverage/Filter.php b/src/Codeception/Coverage/Filter.php index 98c2dd37b9..00e87f9892 100644 --- a/src/Codeception/Coverage/Filter.php +++ b/src/Codeception/Coverage/Filter.php @@ -61,40 +61,31 @@ public function whiteList(array $config): self return $this->newWhiteList($coverage['whitelist']); } - if (isset($coverage['whitelist']['include'])) { - if (!is_array($coverage['whitelist']['include'])) { - throw new ConfigurationException('Error parsing yaml. Config `whitelist: include:` should be an array'); - } - foreach ($coverage['whitelist']['include'] as $fileOrDir) { - $finder = str_contains((string) $fileOrDir, '*') - ? $this->matchWildcardPattern($fileOrDir) - : [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]; - - foreach ($finder as $file) { - $filter->includeFile((string)$file); - } + foreach (['include', 'exclude'] as $type) { + if (!isset($coverage['whitelist'][$type])) { + continue; } - } - if (isset($coverage['whitelist']['exclude'])) { - if (!is_array($coverage['whitelist']['exclude'])) { - throw new ConfigurationException('Error parsing yaml. Config `whitelist: exclude:` should be an array'); + if (!is_array($coverage['whitelist'][$type])) { + throw new ConfigurationException("Error parsing yaml. Config `whitelist: {$type}:` should be an array"); } - foreach ($coverage['whitelist']['exclude'] as $fileOrDir) { + foreach ($coverage['whitelist'][$type] as $fileOrDir) { try { - $finder = str_contains((string) $fileOrDir, '*') + $finder = str_contains($fileOrDir, '*') ? $this->matchWildcardPattern($fileOrDir) : [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir]; foreach ($finder as $file) { - $filter->excludeFile((string)$file); + $file = (string) $file; + $type === 'include' ? $filter->includeFile($file) : $filter->excludeFile($file); } } catch (DirectoryNotFoundException) { continue; } } } + return $this; } @@ -120,35 +111,35 @@ private function newWhiteList(array $whitelist): self ]; } - $allIncludedFiles = []; - foreach ($include as $fileOrDir) { - $finder = str_contains((string) $fileOrDir, '*') - ? $this->matchWildcardPattern($fileOrDir) - : $this->matchFileOrDirectory($fileOrDir); + $allIncludedFiles = $this->matchFiles($include); + $allExcludedFiles = $this->matchFiles($exclude); + + $coveredFiles = array_diff($allIncludedFiles, $allExcludedFiles); - $allIncludedFiles += iterator_to_array($finder->getIterator()); + foreach ($coveredFiles as $coveredFile) { + $this->phpUnitFilter->includeFile((string) $coveredFile); } - $allExcludedFiles = []; - foreach ($exclude as $fileOrDir) { + return $this; + } + + private function matchFiles(array $files): array + { + $matchedFiles = []; + + foreach ($files as $fileOrDir) { try { - $finder = str_contains((string) $fileOrDir, '*') + $finder = str_contains($fileOrDir, '*') ? $this->matchWildcardPattern($fileOrDir) : $this->matchFileOrDirectory($fileOrDir); - $allExcludedFiles += iterator_to_array($finder->getIterator()); + $matchedFiles += iterator_to_array($finder->getIterator()); } catch (DirectoryNotFoundException) { continue; } } - $coveredFiles = array_diff($allIncludedFiles, $allExcludedFiles); - - foreach ($coveredFiles as $coveredFile) { - $this->phpUnitFilter->includeFile((string)$coveredFile); - } - - return $this; + return $matchedFiles; } /** @@ -190,11 +181,8 @@ protected function matchWildcardPattern(string $pattern): Finder $finder->name($file); if ($parts !== []) { $lastPath = array_pop($parts); - if ($lastPath === '*') { - $finder->in(Configuration::projectDir() . implode('/', $parts)); - } else { - $finder->in(Configuration::projectDir() . implode('/', [...$parts, $lastPath])); - } + $path = implode('/', ($lastPath === '*' ? $parts : [...$parts, $lastPath])); + $finder->in(Configuration::projectDir() . $path); } $finder->ignoreVCS(true)->files(); return $finder; diff --git a/src/Codeception/Coverage/PhpCodeCoverageFactory.php b/src/Codeception/Coverage/PhpCodeCoverageFactory.php index 9465032bf4..bac262a308 100644 --- a/src/Codeception/Coverage/PhpCodeCoverageFactory.php +++ b/src/Codeception/Coverage/PhpCodeCoverageFactory.php @@ -19,18 +19,14 @@ public static function build(): CodeCoverage return self::$instance; } - $coverageConfiguration = Configuration::config()['coverage']; - $pathCoverage = $coverageConfiguration['path_coverage'] ?? false; + $coverageConfig = Configuration::config()['coverage']; + $pathCoverage = $coverageConfig['path_coverage'] ?? false; $filter = new CodeCoverageFilter(); - if ($pathCoverage) { - $driver = (new Selector())->forLineAndPathCoverage($filter); - } else { - $driver = (new Selector())->forLineCoverage($filter); - } - self::$instance = new CodeCoverage($driver, $filter); + $selector = new Selector(); + $driver = $pathCoverage ? $selector->forLineAndPathCoverage($filter) : $selector->forLineCoverage($filter); - return self::$instance; + return self::$instance = new CodeCoverage($driver, $filter); } public static function clear(): void diff --git a/src/Codeception/Coverage/Subscriber/Local.php b/src/Codeception/Coverage/Subscriber/Local.php index 0de32083a3..441f0a3aea 100644 --- a/src/Codeception/Coverage/Subscriber/Local.php +++ b/src/Codeception/Coverage/Subscriber/Local.php @@ -12,12 +12,7 @@ use Codeception\Exception\ConfigurationException; use Codeception\Exception\ModuleException; use Codeception\Lib\Interfaces\Remote; -use Codeception\Stub; use Exception; -use PHPUnit\Runner\CodeCoverage as PHPUnitCodeCoverage; -use PHPUnit\Runner\Version as PHPUnitVersion; -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; /** * Collects code coverage from unit and functional tests. diff --git a/src/Codeception/Coverage/Subscriber/LocalServer.php b/src/Codeception/Coverage/Subscriber/LocalServer.php index fa52362505..2b45b912a7 100644 --- a/src/Codeception/Coverage/Subscriber/LocalServer.php +++ b/src/Codeception/Coverage/Subscriber/LocalServer.php @@ -20,16 +20,12 @@ use SebastianBergmann\CodeCoverage\CodeCoverage; use function array_filter; -use function array_key_exists; use function array_replace_recursive; -use function codecept_debug; use function file_exists; use function file_get_contents; -use function is_array; use function json_encode; use function parse_url; use function preg_match; -use function rtrim; use function str_replace; use function stream_context_create; use function unserialize; @@ -79,7 +75,7 @@ class LocalServer extends SuiteSubscriber protected array $c3Access = [ 'http' => [ - 'method' => "GET", + 'method' => 'GET', 'header' => '' ] ]; @@ -113,8 +109,7 @@ public function beforeSuite(SuiteEvent $event): void if ($this->settings['remote_config']) { $this->addC3AccessHeader(self::COVERAGE_HEADER_CONFIG, $this->settings['remote_config']); - $knock = $this->c3Request('clear'); - if ($knock === false) { + if ($this->c3Request('clear') === false) { throw new RemoteException( ' CodeCoverage Error. @@ -149,44 +144,23 @@ public function afterSuite(SuiteEvent $event): void return; } - // wait for all running tests to finish - $blockfilename = Configuration::outputDir() . 'c3tmp/block_report'; - if (file_exists($blockfilename) && filesize($blockfilename) !== 0) { - $retries = 120; // 30 sec total - while (file_get_contents($blockfilename) !== '0' && --$retries >= 0) { - usleep(250_000); // 0.25 sec - } - if (file_get_contents($blockfilename) !== '0' && $retries === -1) { - Notification::warning( - 'Timeout: Some coverage data is not included in the coverage report.', - '', - ); - } - } + $outputDir = Configuration::outputDir() . 'c3tmp/'; + $blockFile = $outputDir . 'block_report'; + $coverageFile = $outputDir . 'codecoverage.serialized'; + $errorFile = $outputDir . 'error.txt'; - $coverageFile = Configuration::outputDir() . 'c3tmp/codecoverage.serialized'; - $retries = 5; - while (!file_exists($coverageFile) && --$retries >= 0) { - $seconds = (int)(0.5 * 1_000_000); // 0.5 sec - usleep($seconds); - } + $this->waitForFile($blockFile, 120, 250_000); + $this->waitForFile($coverageFile, 5, 500_000); if (!file_exists($coverageFile)) { - if (file_exists(Configuration::outputDir() . 'c3tmp/error.txt')) { - throw new RuntimeException(file_get_contents(Configuration::outputDir() . 'c3tmp/error.txt')); - } - - throw new RuntimeException('Code coverage file ' . $coverageFile . ' does not exist'); + throw new RuntimeException( + file_exists($errorFile) ? file_get_contents($errorFile) : "Code coverage file {$coverageFile} does not exist" + ); } - $contents = file_get_contents($coverageFile); - $coverage = @unserialize($contents); - if ($coverage === false) { - return; + if ($coverage = @unserialize(file_get_contents($coverageFile))) { + $this->preProcessCoverage($coverage)->mergeToPrint($coverage); } - - $this->preProcessCoverage($coverage) - ->mergeToPrint($coverage); } /** @@ -194,25 +168,21 @@ public function afterSuite(SuiteEvent $event): void */ protected function preProcessCoverage(CodeCoverage $coverage): self { - //Only Process If Work Directory Set - if ($this->settings['work_dir'] === null) { + if (!$this->settings['work_dir']) { return $this; } $workDir = rtrim((string) $this->settings['work_dir'], '/\\') . DIRECTORY_SEPARATOR; $projectDir = Configuration::projectDir(); - $coverageData = $coverage->getData(true); //We only want covered files, not all whitelisted ones. + $coverageData = $coverage->getData(true); // We only want covered files, not all whitelisted ones. codecept_debug("Replacing all instances of {$workDir} with {$projectDir}"); foreach ($coverageData as $path => $datum) { unset($coverageData[$path]); - $path = str_replace($workDir, $projectDir, (string) $path); - $coverageData[$path] = $datum; } - $coverage->setData($coverageData); return $this; @@ -222,8 +192,8 @@ protected function c3Request(string $action): string|false { $this->addC3AccessHeader(self::COVERAGE_HEADER, 'remote-access'); $context = stream_context_create($this->c3Access); - $c3Url = $this->settings['c3_url'] ?: $this->module->_getUrl(); - $contents = file_get_contents($c3Url . '/c3/report/' . $action, false, $context); + $c3Url = $this->settings['c3_url'] ?? $this->module->_getUrl(); + $contents = file_get_contents("{$c3Url}/c3/report/{$action}", false, $context); $okHeaders = array_filter( $http_response_header, @@ -238,60 +208,52 @@ protected function c3Request(string $action): string|false return $contents; } - protected function startCoverageCollection($testName): void + protected function startCoverageCollection(string $testName): void { - $value = [ + $coverageDataJson = json_encode([ 'CodeCoverage' => $testName, 'CodeCoverage_Suite' => $this->suiteName, 'CodeCoverage_Config' => $this->settings['remote_config'] - ]; - $value = json_encode($value, JSON_THROW_ON_ERROR); + ], JSON_THROW_ON_ERROR); if ($this->module instanceof WebDriverModule) { $this->module->amOnPage('/'); } - $cookieDomain = $this->settings['cookie_domain'] ?? null; + $cookieDomain = $this->settings['cookie_domain'] ?? + parse_url($this->settings['c3_url'] ?? $this->module->_getUrl(), PHP_URL_HOST) ?? + 'localhost'; if (!$cookieDomain) { - $c3Url = parse_url((string) ($this->settings['c3_url'] ?: $this->module->_getUrl())); - // we need to separate coverage cookies by host; we can't separate cookies by port. - $cookieDomain = $c3Url['host'] ?? 'localhost'; - } - - $cookieParams = []; - if ($cookieDomain !== 'localhost') { - $cookieParams['domain'] = $cookieDomain; + $cookieDomain = 'localhost'; } - $this->module->setCookie(self::COVERAGE_COOKIE, $value, $cookieParams); + $cookieParams = $cookieDomain !== 'localhost' ? ['domain' => $cookieDomain] : []; + $this->module->setCookie(self::COVERAGE_COOKIE, $coverageDataJson, $cookieParams); // putting in configuration ensures the cookie is used for all sessions of a MultiSession test $cookies = $this->module->_getConfig('cookies'); - if (!$cookies || !is_array($cookies)) { + if (!is_array($cookies)) { $cookies = []; } - $found = false; + $cookieUpdated = false; foreach ($cookies as &$cookie) { - if (!is_array($cookie) || !array_key_exists('Name', $cookie) || !array_key_exists('Value', $cookie)) { - // \Codeception\Lib\InnerBrowser will complain about this - continue; - } - if ($cookie['Name'] === self::COVERAGE_COOKIE) { - $found = true; - $cookie['Value'] = $value; + if (isset($cookie['Name'], $cookie['Value']) && $cookie['Name'] === self::COVERAGE_COOKIE) { + $cookie['Value'] = $coverageDataJson; + $cookieUpdated = true; break; } + // \Codeception\Lib\InnerBrowser will complain about this } unset($cookie); - if (!$found) { + if (!$cookieUpdated) { $cookies[] = [ 'Name' => self::COVERAGE_COOKIE, - 'Value' => $value + 'Value' => $coverageDataJson ]; } @@ -304,8 +266,7 @@ protected function fetchErrors(): void // @see https://github.com/Codeception/Codeception/issues/1485 if ($this->module instanceof WebDriverModule) { try { - $alert = $this->module->webDriver->switchTo()->alert(); - $alert->getText(); + $this->module->webDriver->switchTo()->alert()->getText(); // If this succeeds an alert is present, abort return; } catch (NoSuchAlertException) { @@ -318,7 +279,7 @@ protected function fetchErrors(): void } catch (ModuleException) { // when a new session is started we can't get cookies because there is no // current page, but there can be no code coverage error either - $error = null; + return; } if (!empty($error)) { $this->module->resetCookie(self::COVERAGE_COOKIE_ERROR); @@ -351,4 +312,17 @@ protected function applySettings(array $settings): void $this->c3Access = array_replace_recursive($this->c3Access, $settings['coverage']['remote_context_options']); } } + + private function waitForFile(string $file, int $maxRetries, int $sleepTime): void + { + $retries = $maxRetries; + while ($retries > 0 && (!file_exists($file) || file_get_contents($file) !== '0')) { + usleep($sleepTime); + $retries--; + } + + if (!file_exists($file) || file_get_contents($file) !== '0') { + Notification::warning('Timeout: Some coverage data is not included in the coverage report.', ''); + } + } } diff --git a/src/Codeception/Coverage/Subscriber/Printer.php b/src/Codeception/Coverage/Subscriber/Printer.php index 1f627fd58c..b1ddabb2da 100644 --- a/src/Codeception/Coverage/Subscriber/Printer.php +++ b/src/Codeception/Coverage/Subscriber/Printer.php @@ -9,7 +9,6 @@ use Codeception\Coverage\PhpCodeCoverageFactory; use Codeception\Event\PrintResultEvent; use Codeception\Events; -use Codeception\Exception\ConfigurationException; use Codeception\Lib\Console\Output; use Codeception\Subscriber\Shared\StaticEventsTrait; use PHPUnit\Runner\Version as PHPUnitVersion; @@ -25,9 +24,8 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function array_merge; -use function class_exists; use function file_put_contents; -use function sprintf; +use function str_starts_with; use function strpos; class Printer implements EventSubscriberInterface @@ -53,7 +51,7 @@ class Printer implements EventSubscriberInterface protected string $logDir; - public function __construct(protected array $options, private Output $output) + public function __construct(protected array $options, private readonly Output $output) { $this->logDir = Configuration::outputDir(); $this->settings = array_merge($this->settings, Configuration::config()['coverage']); @@ -68,7 +66,7 @@ public function __construct(protected array $options, private Output $output) protected function absolutePath(string $path): string { - if ((str_starts_with($path, '/')) || (strpos($path, ':') === 1)) { // absolute path + if (str_starts_with($path, '/') || strpos($path, ':') === 1) { // absolute path return $path; } return $this->logDir . $path; @@ -87,76 +85,33 @@ public function printResult(PrintResultEvent $event): void $this->output->write("Remote CodeCoverage reports are not printed to console\n"); $this->printPHP(); $this->output->write("\n"); - if ($this->options['coverage-html']) { - $this->printHtml(); - $this->output->write("HTML report generated in {$this->options['coverage-html']}\n"); - } - if ($this->options['coverage-xml']) { - $this->printXml(); - $this->output->write("XML report generated in {$this->options['coverage-xml']}\n"); - } - if ($this->options['coverage-text']) { - $this->printText(); - $this->output->write("Text report generated in {$this->options['coverage-text']}\n"); - } - if ($this->options['coverage-crap4j']) { - $this->printCrap4j(); - $this->output->write("Crap4j report generated in {$this->options['coverage-crap4j']}\n"); - } - if ($this->options['coverage-cobertura']) { - $this->printCobertura(); - $this->output->write("Cobertura report generated in {$this->options['coverage-cobertura']}\n"); - } - if ($this->options['coverage-phpunit']) { - $this->printPHPUnit(); - $this->output->write("PHPUnit report generated in {$this->options['coverage-phpunit']}\n"); + + $reports = [ + 'HTML' => ['name' => 'coverage-html', 'method' => 'printHTML'], + 'XML' => ['name' => 'coverage-xml', 'method' => 'printXML'], + 'Text' => ['name' => 'coverage-text', 'method' => 'printText'], + 'Crap4j' => ['name' => 'coverage-crap4j', 'method' => 'printCrap4j'], + 'Cobertura' => ['name' => 'coverage-cobertura', 'method' => 'printCobertura'], + 'PHPUnit' => ['name' => 'coverage-phpunit', 'method' => 'printPHPUnit'], + ]; + + foreach ($reports as $reportType => $reportData) { + if ($option = $this->options[$reportData['name']]) { + $this->{$reportData['method']}(); + $this->output->write("{$reportType} report generated in {$option}\n"); + } } } protected function printConsole(): void { - if (PHPUnitVersion::series() < 10) { - $writer = new TextReport( - $this->settings['low_limit'], - $this->settings['high_limit'], - $this->settings['show_uncovered'], - $this->settings['show_only_summary'] - ); - } else { - $writer = new TextReport( - Thresholds::from( - $this->settings['low_limit'], - $this->settings['high_limit'], - ), - $this->settings['show_uncovered'], - $this->settings['show_only_summary'] - ); - } + $writer = $this->createTextWriter(); $this->output->write($writer->process(self::$coverage, $this->options['colors'])); } protected function printHtml(): void { - if (PHPUnitVersion::series() < 10) { - $writer = new HtmlFacadeReport( - $this->settings['low_limit'], - $this->settings['high_limit'], - sprintf( - ', Codeception and PHPUnit %s', - PHPUnitVersion::id() - ) - ); - } else { - $writer = new HtmlFacadeReport( - sprintf( - ', Codeception and PHPUnit %s', - PHPUnitVersion::id() - ), - null, - Thresholds::from($this->settings['low_limit'], $this->settings['high_limit']), - ); - } - + $writer = $this->createHtmlFacadeWriter(); $writer->process(self::$coverage, $this->absolutePath($this->options['coverage-html'])); } @@ -174,27 +129,10 @@ protected function printPHP(): void protected function printText(): void { - if (PHPUnitVersion::series() < 10) { - $writer = new TextReport( - $this->settings['low_limit'], - $this->settings['high_limit'], - $this->settings['show_uncovered'], - $this->settings['show_only_summary'] - ); - } else { - $writer = new TextReport( - Thresholds::from( - $this->settings['low_limit'], - $this->settings['high_limit'], - ), - $this->settings['show_uncovered'], - $this->settings['show_only_summary'] - ); - } - + $writer = $this->createTextWriter(); file_put_contents( $this->absolutePath($this->options['coverage-text']), - $writer->process(self::$coverage, false) + $writer->process(self::$coverage) ); } @@ -215,4 +153,19 @@ protected function printPHPUnit(): void $writer = new XmlFacadeReport(PHPUnitVersion::id()); $writer->process(self::$coverage, $this->absolutePath($this->options['coverage-phpunit'])); } + + private function createHtmlFacadeWriter(): HtmlFacadeReport + { + $generator = ', Codeception and PHPUnit {PHPUnitVersion::id()}'; + return PHPUnitVersion::series() < 10 ? + new HtmlFacadeReport($this->settings['low_limit'], $this->settings['high_limit'], $generator) : + new HtmlFacadeReport($generator, null, Thresholds::from($this->settings['low_limit'], $this->settings['high_limit'])); + } + + private function createTextWriter(): TextReport + { + return PHPUnitVersion::series() < 10 ? + new TextReport($this->settings['low_limit'], $this->settings['high_limit'], $this->settings['show_uncovered'], $this->settings['show_only_summary']) : + new TextReport(Thresholds::from($this->settings['low_limit'], $this->settings['high_limit']), $this->settings['show_uncovered'], $this->settings['show_only_summary']); + } } diff --git a/src/Codeception/Coverage/Subscriber/RemoteServer.php b/src/Codeception/Coverage/Subscriber/RemoteServer.php index 98bcb9e92d..0a8cf446e6 100644 --- a/src/Codeception/Coverage/Subscriber/RemoteServer.php +++ b/src/Codeception/Coverage/Subscriber/RemoteServer.php @@ -36,67 +36,37 @@ public function afterSuite(SuiteEvent $event): void if (!$this->isEnabled()) { return; } - $suite = strtr($event->getSuite()->getName(), ['\\' => '.']); + if ($this->options['coverage-xml']) { - $this->retrieveAndPrintXml($suite); + $this->retrieveAndPrint('clover', $suite, '.remote.coverage.xml'); } if ($this->options['coverage-html']) { - $this->retrieveAndPrintHtml($suite); + $this->retrieveToTempFileAndPrint('html', $suite, '.remote.coverage'); } if ($this->options['coverage-crap4j']) { - $this->retrieveAndPrintCrap4j($suite); + $this->retrieveAndPrint('crap4j', $suite, '.remote.crap4j.xml'); } if ($this->options['coverage-cobertura']) { - $this->retrieveAndPrintCobertura($suite); + $this->retrieveAndPrint('cobertura', $suite, '.remote.cobertura.xml'); } if ($this->options['coverage-phpunit']) { - $this->retrieveAndPrintPHPUnit($suite); - } - } - - protected function retrieveAndPrintHtml(string $suite): void - { - $tempFile = tempnam(sys_get_temp_dir(), 'C3') . '.tar'; - file_put_contents($tempFile, $this->c3Request('html')); - - $destDir = Configuration::outputDir() . $suite . '.remote.coverage'; - if (is_dir($destDir)) { - FileSystem::doEmptyDir($destDir); - } else { - mkdir($destDir, 0777, true); + $this->retrieveToTempFileAndPrint('phpunit', $suite, '.remote.coverage-phpunit'); } - - $pharData = new PharData($tempFile); - $pharData->extractTo($destDir); - - unlink($tempFile); - } - - protected function retrieveAndPrintXml(string $suite): void - { - $destFile = Configuration::outputDir() . $suite . '.remote.coverage.xml'; - file_put_contents($destFile, $this->c3Request('clover')); - } - - protected function retrieveAndPrintCrap4j(string $suite): void - { - $destFile = Configuration::outputDir() . $suite . '.remote.crap4j.xml'; - file_put_contents($destFile, $this->c3Request('crap4j')); } - protected function retrieveAndPrintCobertura(string $suite): void + protected function retrieveAndPrint(string $type, string $suite, string $extension): void { - $destFile = Configuration::outputDir() . $suite . '.remote.cobertura.xml'; - file_put_contents($destFile, $this->c3Request('cobertura')); + $destFile = Configuration::outputDir() . $suite . $extension; + file_put_contents($destFile, $this->c3Request($type)); } - protected function retrieveAndPrintPHPUnit(string $suite): void + protected function retrieveToTempFileAndPrint(string $type, string $suite, string $extension): void { $tempFile = tempnam(sys_get_temp_dir(), 'C3') . '.tar'; - file_put_contents($tempFile, $this->c3Request('phpunit')); + file_put_contents($tempFile, $this->c3Request($type)); - $destDir = Configuration::outputDir() . $suite . '.remote.coverage-phpunit'; + $destDir = Configuration::outputDir() . $suite . $extension; if (is_dir($destDir)) { FileSystem::doEmptyDir($destDir); } else { diff --git a/src/Codeception/Coverage/SuiteSubscriber.php b/src/Codeception/Coverage/SuiteSubscriber.php index 1316450147..647ae44ccc 100644 --- a/src/Codeception/Coverage/SuiteSubscriber.php +++ b/src/Codeception/Coverage/SuiteSubscriber.php @@ -84,6 +84,11 @@ protected function applySettings(array $settings): void } } + $this->configureCoverage(); + } + + protected function configureCoverage(): void + { if ($this->settings['strict_covers_annotation']) { $this->coverage->enableCheckForUnintentionallyCoveredCode(); } From 16621cca659537a7e70a50b3a82d4d221f1639b2 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Wed, 10 Jul 2024 20:16:13 +0200 Subject: [PATCH 06/25] generate:cest: Adding `declare(strict_types=1);` and return type `void` to generated files (#6736) * Update Cest.php: Adding `declare(strict_types=1);` and return type `void` to generated files * Adding `_before` explanations * Simplify changes --------- Co-authored-by: Aaron Gustavo Nieves --- src/Codeception/Lib/Generator/Cest.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Codeception/Lib/Generator/Cest.php b/src/Codeception/Lib/Generator/Cest.php index d3e9baed49..8c09ddcf32 100644 --- a/src/Codeception/Lib/Generator/Cest.php +++ b/src/Codeception/Lib/Generator/Cest.php @@ -17,17 +17,20 @@ class Cest protected string $template = << Date: Tue, 23 Jul 2024 08:17:19 -0500 Subject: [PATCH 07/25] Simplify command classes (#6750) --- composer.json | 16 +- src/Codeception/Command/Bootstrap.php | 34 ++-- src/Codeception/Command/Build.php | 6 +- src/Codeception/Command/Clean.php | 14 +- src/Codeception/Command/Completion.php | 3 +- .../Command/CompletionFallback.php | 2 +- src/Codeception/Command/ConfigValidate.php | 28 ++-- src/Codeception/Command/Console.php | 19 +-- src/Codeception/Command/DryRun.php | 9 +- src/Codeception/Command/GenerateCest.php | 20 +-- .../Command/GenerateEnvironment.php | 27 ++-- src/Codeception/Command/GenerateFeature.php | 20 +-- src/Codeception/Command/GenerateGroup.php | 16 +- src/Codeception/Command/GenerateHelper.php | 20 +-- .../Command/GeneratePageObject.php | 19 +-- src/Codeception/Command/GenerateScenarios.php | 33 ++-- src/Codeception/Command/GenerateSnapshot.php | 19 +-- .../Command/GenerateStepObject.php | 25 +-- src/Codeception/Command/GenerateSuite.php | 48 ++---- src/Codeception/Command/GenerateTest.php | 26 +-- src/Codeception/Command/GherkinSnippets.php | 27 +--- src/Codeception/Command/GherkinSteps.php | 26 +-- src/Codeception/Command/Init.php | 37 ++--- src/Codeception/Command/Run.php | 151 +++++------------- src/Codeception/Command/SelfUpdate.php | 49 ++---- .../Command/Shared/ConfigTrait.php | 2 +- 26 files changed, 221 insertions(+), 475 deletions(-) diff --git a/composer.json b/composer.json index a7783ae478..8e14508e4d 100644 --- a/composer.json +++ b/composer.json @@ -28,12 +28,12 @@ "phpunit/php-timer": "^5.0.3 || ^6.0 || ^7.0", "sebastian/comparator": "^4.0.5 || ^5.0 || ^6.0", "sebastian/diff": "^4.0.3 || ^5.0 || ^6.0", - "symfony/console": ">=4.4.24 <8.0", - "symfony/css-selector": ">=4.4.24 <8.0", - "symfony/event-dispatcher": ">=4.4.24 <8.0", - "symfony/finder": ">=4.4.24 <8.0", - "symfony/yaml": ">=4.4.24 <8.0", - "symfony/var-dumper": ">=4.4.24 <8.0", + "symfony/console": ">=5.4.24 <8.0", + "symfony/css-selector": ">=5.4.24 <8.0", + "symfony/event-dispatcher": ">=5.4.24 <8.0", + "symfony/finder": ">=5.4.24 <8.0", + "symfony/yaml": ">=5.4.24 <8.0", + "symfony/var-dumper": ">=5.4.24 <8.0", "psy/psysh": "^0.11.2 || ^0.12" }, "require-dev": { @@ -46,8 +46,8 @@ "codeception/module-filesystem": "*@dev", "codeception/module-phpbrowser": "*@dev", "codeception/util-universalframework": "*@dev", - "symfony/process": ">=4.4.24 <8.0", - "symfony/dotenv": ">=4.4.24 <8.0", + "symfony/process": ">=5.4.24 <8.0", + "symfony/dotenv": ">=5.4.24 <8.0", "vlucas/phpdotenv": "^5.1", "jetbrains/phpstorm-attributes": "^1.0" }, diff --git a/src/Codeception/Command/Bootstrap.php b/src/Codeception/Command/Bootstrap.php index 510bb00568..5e208c0c13 100644 --- a/src/Codeception/Command/Bootstrap.php +++ b/src/Codeception/Command/Bootstrap.php @@ -15,7 +15,7 @@ * Creates default config, tests directory and sample suites for current project. * Use this command to start building a test suite. * - * By default it will create 3 suites **Acceptance**, **Functional**, and **Unit**. + * By default, it will create 3 suites **Acceptance**, **Functional**, and **Unit**. * * * `codecept bootstrap` - creates `tests` dir and `codeception.yml` in current dir. * * `codecept bootstrap --empty` - creates `tests` dir without suites @@ -28,33 +28,21 @@ class Bootstrap extends Command { protected function configure(): void { - $this->setDefinition( - [ - new InputArgument('path', InputArgument::OPTIONAL, 'custom installation dir', null), - new InputOption( - 'namespace', - 's', - InputOption::VALUE_OPTIONAL, - 'Namespace to add for actor classes and helpers' - ), - new InputOption('actor', 'a', InputOption::VALUE_OPTIONAL, 'Custom actor instead of Tester'), - new InputOption('empty', 'e', InputOption::VALUE_NONE, "Don't create standard suites") - ] - ); + $this->setDescription('Creates default test suites and generates all required files') + ->addArgument('path', InputArgument::OPTIONAL, 'custom installation dir') + ->addOption('namespace', 's', InputOption::VALUE_OPTIONAL, 'Namespace to add for actor classes and helpers') + ->addOption('actor', 'a', InputOption::VALUE_OPTIONAL, 'Custom actor instead of Tester') + ->addOption('empty', 'e', InputOption::VALUE_NONE, "Don't create standard suites"); } - public function getDescription(): string - { - return "Creates default test suites and generates all required files"; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $bootstrap = new BootstrapTemplate($input, $output); - if ($input->getArgument('path')) { - $bootstrap->initDir($input->getArgument('path')); + if ($path = $input->getArgument('path')) { + $bootstrap->initDir($path); } + $bootstrap->setup(); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/Build.php b/src/Codeception/Command/Build.php index 6e49f19ce3..c8721f8d92 100644 --- a/src/Codeception/Command/Build.php +++ b/src/Codeception/Command/Build.php @@ -39,7 +39,7 @@ protected function execute(InputInterface $input, SymfonyOutputInterface $output { $this->output = $output; $this->buildActorsForConfig(); - return 0; + return Command::SUCCESS; } private function buildActor(array $settings): bool @@ -65,7 +65,7 @@ private function buildActions(array $settings): bool $actionsGenerator = new ActionsGenerator($settings); $content = $actionsGenerator->produce(); $this->output->writeln( - " -> {$settings['actor']}Actions.php generated successfully. " + sprintf(' -> %sActions.php generated successfully. ', $settings['actor']) . $actionsGenerator->getNumMethods() . " methods added" ); @@ -89,7 +89,7 @@ private function buildSuiteActors(): void $actorBuilt = $this->buildActor($settings); if ($actorBuilt) { - $this->output->writeln("{$settings['actor']}.php created."); + $this->output->writeln($settings['actor'] . '.php created.'); } } } diff --git a/src/Codeception/Command/Clean.php b/src/Codeception/Command/Clean.php index 10758057fa..c77872b397 100644 --- a/src/Codeception/Command/Clean.php +++ b/src/Codeception/Command/Clean.php @@ -20,30 +20,28 @@ class Clean extends Command { use Shared\ConfigTrait; - public function getDescription(): string + protected function configure(): void { - return 'Recursively cleans log and generated code'; + $this->setDescription('Recursively cleans log and generated code'); } protected function execute(InputInterface $input, OutputInterface $output): int { - $projectDir = Configuration::projectDir(); - $this->cleanProjectsRecursively($output, $projectDir); + $this->cleanProjectsRecursively($output, Configuration::projectDir()); $output->writeln("Done"); - return 0; + return Command::SUCCESS; } private function cleanProjectsRecursively(OutputInterface $output, string $projectDir): void { $config = Configuration::config($projectDir); $logDir = Configuration::outputDir(); - $output->writeln("Cleaning up output " . $logDir . "..."); + $output->writeln(sprintf('Cleaning up output %s...', $logDir)); FileSystem::doEmptyDir($logDir); $subProjects = $config['include']; foreach ($subProjects as $subProject) { - $subProjectDir = $projectDir . $subProject; - $this->cleanProjectsRecursively($output, $subProjectDir); + $this->cleanProjectsRecursively($output, $projectDir . $subProject); } } } diff --git a/src/Codeception/Command/Completion.php b/src/Codeception/Command/Completion.php index dd71e0cf55..26b13b2a4a 100644 --- a/src/Codeception/Command/Completion.php +++ b/src/Codeception/Command/Completion.php @@ -10,6 +10,7 @@ use Stecman\Component\Symfony\Console\BashCompletion\Completion\ShellPathCompletion; use Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand; use Stecman\Component\Symfony\Console\BashCompletion\CompletionHandler; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputDefinition as SymfonyInputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -72,7 +73,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } parent::execute($input, $output); - return 0; + return Command::SUCCESS; } protected function createDefinition(): SymfonyInputDefinition diff --git a/src/Codeception/Command/CompletionFallback.php b/src/Codeception/Command/CompletionFallback.php index b45a8604a6..9f76dd971b 100644 --- a/src/Codeception/Command/CompletionFallback.php +++ b/src/Codeception/Command/CompletionFallback.php @@ -31,6 +31,6 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln("Install optional stecman/symfony-console-completion"); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/ConfigValidate.php b/src/Codeception/Command/ConfigValidate.php index 84e7e06d30..6719d1d63f 100644 --- a/src/Codeception/Command/ConfigValidate.php +++ b/src/Codeception/Command/ConfigValidate.php @@ -46,22 +46,13 @@ class ConfigValidate extends Command protected function configure(): void { - $this->setDefinition( - [ - new InputArgument('suite', InputArgument::OPTIONAL, 'to show suite configuration'), - new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), - new InputOption('override', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Override config values'), - ] - ); - parent::configure(); + $this->setDescription('Validates and prints config to screen') + ->addArgument('suite', InputArgument::OPTIONAL, 'To show suite configuration') + ->addOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config') + ->addOption('override', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Override config values'); } - public function getDescription(): string - { - return 'Validates and prints config to screen'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); @@ -72,8 +63,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $output->writeln("------------------------------\n"); $output->writeln("{$suite} Suite Config:\n"); $output->writeln($this->formatOutput($config)); - - return 0; + return Command::SUCCESS; } $output->write("Validating global config... "); @@ -82,8 +72,9 @@ public function execute(InputInterface $input, OutputInterface $output): int if (!empty($input->getOption('override'))) { $config = $this->overrideConfig($input->getOption('override')); } - $suites = Configuration::suites(); + $output->writeln("Ok"); + $suites = Configuration::suites(); $output->writeln("------------------------------\n"); $output->writeln("Codeception Config:\n"); @@ -104,7 +95,8 @@ public function execute(InputInterface $input, OutputInterface $output): int } $output->writeln("Execute codecept config:validate [] to see config for a suite"); - return 0; + + return Command::SUCCESS; } protected function formatOutput($config): ?string diff --git a/src/Codeception/Command/Console.php b/src/Codeception/Command/Console.php index a3fe75855c..cebea00dbd 100644 --- a/src/Codeception/Command/Console.php +++ b/src/Codeception/Command/Console.php @@ -49,20 +49,12 @@ class Console extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::REQUIRED, 'suite to be executed'), - new InputOption('colors', '', InputOption::VALUE_NONE, 'Use colors in output'), - ]); - - parent::configure(); - } - - public function getDescription(): string - { - return 'Launches interactive test console'; + $this->setDescription('Launches interactive test console') + ->addArgument('suite', InputArgument::REQUIRED, 'suite to be executed') + ->addOption('colors', '', InputOption::VALUE_NONE, 'Use colors in output'); } - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $suiteName = $input->getArgument('suite'); $this->output = $output; @@ -103,6 +95,7 @@ public function execute(InputInterface $input, OutputInterface $output): int if (isset($config['namespace']) && $config['namespace'] !== '') { $settings['actor'] = $config['namespace'] . '\\Support\\' . $settings['actor']; } + $actor = $settings['actor']; $I = new $actor($scenario); @@ -126,7 +119,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $eventDispatcher->dispatch(new SuiteEvent($this->suite), Events::SUITE_AFTER); $output->writeln("Bye-bye!"); - return 0; + return Command::SUCCESS; } protected function listenToSignals(): void diff --git a/src/Codeception/Command/DryRun.php b/src/Codeception/Command/DryRun.php index b49c55070c..73d8b4a8d3 100644 --- a/src/Codeception/Command/DryRun.php +++ b/src/Codeception/Command/DryRun.php @@ -18,6 +18,7 @@ use Codeception\Test\Test; use Exception; use InvalidArgumentException; +use ReflectionClass; use ReflectionIntersectionType; use ReflectionMethod; use ReflectionNamedType; @@ -62,7 +63,7 @@ public function getDescription(): string return 'Prints step-by-step scenario-driven test or a feature'; } - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); $suite = (string)$input->getArgument('suite'); @@ -106,7 +107,7 @@ public function execute(InputInterface $input, OutputInterface $output): int return 0; } - protected function matchTestFromFilename($filename, $testsPath) + protected function matchTestFromFilename($filename, $testsPath): array { $filename = str_replace(['//', '\/', '\\'], '/', $filename); $res = preg_match("#^{$testsPath}/(.*?)/(.*)$#", $filename, $matches); @@ -143,7 +144,7 @@ protected function dryRunTest(OutputInterface $output, EventDispatcher $eventDis private function mockModule(string $moduleName, ModuleContainer $moduleContainer): void { $module = $moduleContainer->getModule($moduleName); - $class = new \ReflectionClass($module); + $class = new ReflectionClass($module); $methodResults = []; foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { if ($method->isConstructor()) { @@ -155,7 +156,7 @@ private function mockModule(string $moduleName, ModuleContainer $moduleContainer $moduleContainer->mock($moduleName, Stub::makeEmpty($module, $methodResults)); } - private function getDefaultResultForMethod(\ReflectionClass $class, ReflectionMethod $method): mixed + private function getDefaultResultForMethod(ReflectionClass $class, ReflectionMethod $method): mixed { $returnType = $method->getReturnType(); diff --git a/src/Codeception/Command/GenerateCest.php b/src/Codeception/Command/GenerateCest.php index 6ddac7902f..aff87c6aae 100644 --- a/src/Codeception/Command/GenerateCest.php +++ b/src/Codeception/Command/GenerateCest.php @@ -28,18 +28,12 @@ class GenerateCest extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::REQUIRED, 'suite where tests will be put'), - new InputArgument('class', InputArgument::REQUIRED, 'test name'), - ]); + $this->setDescription('Generates empty Cest file in suite') + ->addArgument('suite', InputArgument::REQUIRED, 'suite where tests will be put') + ->addArgument('class', InputArgument::REQUIRED, 'test name'); } - public function getDescription(): string - { - return 'Generates empty Cest file in suite'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $suite = $input->getArgument('suite'); $class = $input->getArgument('class'); @@ -53,16 +47,16 @@ public function execute(InputInterface $input, OutputInterface $output): int if (file_exists($filename)) { $output->writeln("Test {$filename} already exists"); - return 1; + return Command::FAILURE; } $cest = new CestGenerator($class, $config); $res = $this->createFile($filename, $cest->produce()); if (!$res) { $output->writeln("Test {$filename} already exists"); - return 1; + return Command::FAILURE; } $output->writeln("Test was created in {$filename}"); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/GenerateEnvironment.php b/src/Codeception/Command/GenerateEnvironment.php index fe5c648d75..024974099c 100644 --- a/src/Codeception/Command/GenerateEnvironment.php +++ b/src/Codeception/Command/GenerateEnvironment.php @@ -25,17 +25,11 @@ class GenerateEnvironment extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('env', InputArgument::REQUIRED, 'Environment name'), - ]); + $this->setDescription('Generates empty environment config') + ->addArgument('env', InputArgument::REQUIRED, 'Environment name'); } - public function getDescription(): string - { - return 'Generates empty environment config'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $config = $this->getGlobalConfig(); if (Configuration::envsDir() === '') { @@ -45,19 +39,20 @@ public function execute(InputInterface $input, OutputInterface $output): int . "envs: tests/_envs" ); } + $relativePath = $config['paths']['envs']; $env = $input->getArgument('env'); - $file = "{$env}.yml"; + $file = $env . '.yml'; $path = $this->createDirectoryFor($relativePath, $file); - $saved = $this->createFile($path . $file, "# `{$env}` environment config goes here"); + $saved = $this->createFile($path . $file, sprintf('# `%s` environment config goes here', $env)); if ($saved) { - $output->writeln("{$env} config was created in {$relativePath}/{$file}"); - return 0; - } else { - $output->writeln("File {$relativePath}/{$file} already exists"); - return 1; + $output->writeln(sprintf('%s config was created in %s/%s', $env, $relativePath, $file)); + return Command::SUCCESS; } + + $output->writeln(sprintf('File %s/%s already exists', $relativePath, $file)); + return Command::FAILURE; } } diff --git a/src/Codeception/Command/GenerateFeature.php b/src/Codeception/Command/GenerateFeature.php index b0e1c5feed..18356bb43a 100644 --- a/src/Codeception/Command/GenerateFeature.php +++ b/src/Codeception/Command/GenerateFeature.php @@ -30,19 +30,13 @@ class GenerateFeature extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::REQUIRED, 'suite to be tested'), - new InputArgument('feature', InputArgument::REQUIRED, 'feature to be generated'), - new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), - ]); + $this->setDescription('Generates empty feature file in suite') + ->addArgument('suite', InputArgument::REQUIRED, 'suite to be tested') + ->addArgument('feature', InputArgument::REQUIRED, 'feature to be generated') + ->addOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'); } - public function getDescription(): string - { - return 'Generates empty feature file in suite'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $suite = $input->getArgument('suite'); $filename = (string)$input->getArgument('feature'); @@ -58,9 +52,9 @@ public function execute(InputInterface $input, OutputInterface $output): int $res = $this->createFile($fullPath, $feature->produce()); if (!$res) { $output->writeln("Feature {$filename} already exists"); - return 1; + return Command::FAILURE; } $output->writeln("Feature was created in {$fullPath}"); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/GenerateGroup.php b/src/Codeception/Command/GenerateGroup.php index 7c7ef1310d..0456264411 100644 --- a/src/Codeception/Command/GenerateGroup.php +++ b/src/Codeception/Command/GenerateGroup.php @@ -25,17 +25,11 @@ class GenerateGroup extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('group', InputArgument::REQUIRED, 'Group class name'), - ]); + $this->setDescription('Generates Group subscriber') + ->addArgument('group', InputArgument::REQUIRED, 'Group class name'); } - public function getDescription(): string - { - return 'Generates Group subscriber'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $config = $this->getGlobalConfig(); $groupInputArgument = (string)$input->getArgument('group'); @@ -50,13 +44,13 @@ public function execute(InputInterface $input, OutputInterface $output): int if (!$res) { $output->writeln("Group {$filename} already exists"); - return 1; + return Command::FAILURE; } $output->writeln("Group extension was created in {$filename}"); $output->writeln( 'To use this group extension, include it to "extensions" option of global Codeception config.' ); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/GenerateHelper.php b/src/Codeception/Command/GenerateHelper.php index f265b4890d..cc45e893cb 100644 --- a/src/Codeception/Command/GenerateHelper.php +++ b/src/Codeception/Command/GenerateHelper.php @@ -27,17 +27,11 @@ class GenerateHelper extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('name', InputArgument::REQUIRED, 'helper name'), - ]); + $this->setDescription('Generates a new helper') + ->addArgument('name', InputArgument::REQUIRED, 'Helper name'); } - public function getDescription(): string - { - return 'Generates new helper'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $name = ucfirst((string)$input->getArgument('name')); $config = $this->getGlobalConfig(); @@ -48,10 +42,10 @@ public function execute(InputInterface $input, OutputInterface $output): int $res = $this->createFile($filename, (new Helper($config, $name))->produce()); if ($res) { $output->writeln("Helper {$filename} created"); - return 0; - } else { - $output->writeln("Error creating helper {$filename}"); - return 1; + return Command::SUCCESS; } + + $output->writeln(sprintf('Error creating helper %s', $filename)); + return Command::FAILURE; } } diff --git a/src/Codeception/Command/GeneratePageObject.php b/src/Codeception/Command/GeneratePageObject.php index 053880cb32..6e067aacd3 100644 --- a/src/Codeception/Command/GeneratePageObject.php +++ b/src/Codeception/Command/GeneratePageObject.php @@ -28,19 +28,12 @@ class GeneratePageObject extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::REQUIRED, 'Either suite name or page object name)'), - new InputArgument('page', InputArgument::OPTIONAL, 'Page name of pageobject to represent'), - ]); - parent::configure(); + $this->setDescription('Generates empty PageObject class') + ->addArgument('suite', InputArgument::REQUIRED, 'Either suite name or page object name') + ->addArgument('page', InputArgument::OPTIONAL, 'Page name of pageobject to represent'); } - public function getDescription(): string - { - return 'Generates empty PageObject class'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $suite = (string)$input->getArgument('suite'); $class = $input->getArgument('page'); @@ -69,9 +62,9 @@ public function execute(InputInterface $input, OutputInterface $output): int if (!$res) { $output->writeln("PageObject {$filename} already exists"); - return 1; + return Command::FAILURE; } $output->writeln("PageObject was created in {$filename}"); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/GenerateScenarios.php b/src/Codeception/Command/GenerateScenarios.php index 27249e1f45..85ed70c0ed 100644 --- a/src/Codeception/Command/GenerateScenarios.php +++ b/src/Codeception/Command/GenerateScenarios.php @@ -37,18 +37,11 @@ class GenerateScenarios extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::REQUIRED, 'suite from which texts should be generated'), - new InputOption('path', 'p', InputOption::VALUE_REQUIRED, 'Use specified path as destination instead of default'), - new InputOption('single-file', '', InputOption::VALUE_NONE, 'Render all scenarios to only one file'), - new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Specify output format: html or text (default)', 'text'), - ]); - parent::configure(); - } - - public function getDescription(): string - { - return 'Generates text representation for all scenarios'; + $this->setDescription('Generates text representation for all scenarios') + ->addArgument('suite', InputArgument::REQUIRED, 'suite from which texts should be generated') + ->addOption('path', 'p', InputOption::VALUE_REQUIRED, 'Use specified path as destination instead of default') + ->addOption('single-file', '', InputOption::VALUE_NONE, 'Render all scenarios to only one file') + ->addOption('format', 'f', InputOption::VALUE_REQUIRED, 'Specify output format: html or text (default)', 'text'); } protected function execute(InputInterface $input, OutputInterface $output): int @@ -61,7 +54,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $format = $input->getOption('format'); - @mkdir($path); + @mkdir($path, 0777, true); if (!is_writable($path)) { throw new ConfigurationException( @@ -69,7 +62,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int ); } - $path = $path . DIRECTORY_SEPARATOR . $suite; + $path .= DIRECTORY_SEPARATOR . $suite; if (!$input->getOption('single-file')) { @mkdir($path); } @@ -81,7 +74,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $tests = $this->getTests($suiteManager); - $scenarios = ""; + $scenarios = ''; foreach ($tests as $test) { if (!$test instanceof ScenarioDriven || !$test instanceof Descriptive) { @@ -109,13 +102,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($input->getOption('single-file')) { $this->createFile($path . $this->formatExtension($format), $this->decorate($scenarios, $format), true); } - return 0; + + return Command::SUCCESS; } protected function decorate(string $text, string $format): string { if ($format === 'html') { - return "$text"; + return "{$text}"; } return $text; } @@ -128,10 +122,7 @@ protected function getTests($suiteManager) protected function formatExtension(string $format): string { - if ($format === 'html') { - return '.html'; - } - return '.txt'; + return '.' . ($format === 'html' ? 'html' : 'txt'); } private function underscore(string $name): string diff --git a/src/Codeception/Command/GenerateSnapshot.php b/src/Codeception/Command/GenerateSnapshot.php index 171c957201..ad91a001a2 100644 --- a/src/Codeception/Command/GenerateSnapshot.php +++ b/src/Codeception/Command/GenerateSnapshot.php @@ -29,19 +29,12 @@ class GenerateSnapshot extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::REQUIRED, 'Suite name or snapshot name)'), - new InputArgument('snapshot', InputArgument::OPTIONAL, 'Name of snapshot'), - ]); - parent::configure(); + $this->setDescription('Generates empty Snapshot class') + ->addArgument('suite', InputArgument::REQUIRED, 'Suite name or snapshot name)') + ->addArgument('snapshot', InputArgument::OPTIONAL, 'Name of snapshot'); } - public function getDescription(): string - { - return 'Generates empty Snapshot class'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $suite = (string)$input->getArgument('suite'); $class = $input->getArgument('snapshot'); @@ -70,9 +63,9 @@ public function execute(InputInterface $input, OutputInterface $output): int if (!$res) { $output->writeln("Snapshot {$filename} already exists"); - return 1; + return Command::FAILURE; } $output->writeln("Snapshot was created in {$filename}"); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/GenerateStepObject.php b/src/Codeception/Command/GenerateStepObject.php index ef1b1bdc29..d790bb01a6 100644 --- a/src/Codeception/Command/GenerateStepObject.php +++ b/src/Codeception/Command/GenerateStepObject.php @@ -30,32 +30,23 @@ class GenerateStepObject extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::REQUIRED, 'Suite for StepObject'), - new InputArgument('step', InputArgument::REQUIRED, 'StepObject name'), - new InputOption('silent', '', InputOption::VALUE_NONE, 'skip verification question'), - ]); + $this->setDescription('Generates empty StepObject class') + ->addArgument('suite', InputArgument::REQUIRED, 'Suite for StepObject') + ->addArgument('step', InputArgument::REQUIRED, 'StepObject name') + ->addOption('silent', '', InputOption::VALUE_NONE, 'Skip verification question'); } - public function getDescription(): string - { - return 'Generates empty StepObject class'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $suite = (string)$input->getArgument('suite'); $step = $input->getArgument('step'); $config = $this->getSuiteConfig($suite); - $class = $this->getShortClassName($step); - $path = $this->createDirectoryFor(Configuration::supportDir() . 'Step' . DIRECTORY_SEPARATOR . ucfirst($suite), $step); /** @var QuestionHelper $dialog */ - $dialog = $this->getHelperSet()->get('question'); + $dialog = $this->getHelper('question'); $filename = $path . $class . '.php'; - $stepObject = new StepObjectGenerator($config, ucfirst($suite) . '\\' . $step); if (!$input->getOption('silent')) { @@ -72,9 +63,9 @@ public function execute(InputInterface $input, OutputInterface $output): int if (!$res) { $output->writeln("StepObject {$filename} already exists"); - return 1; + return Command::FAILURE; } $output->writeln("StepObject was created in {$filename}"); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/GenerateSuite.php b/src/Codeception/Command/GenerateSuite.php index ea82c217fe..606a039448 100644 --- a/src/Codeception/Command/GenerateSuite.php +++ b/src/Codeception/Command/GenerateSuite.php @@ -35,31 +35,21 @@ class GenerateSuite extends Command protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::REQUIRED, 'suite to be generated'), - new InputArgument('actor', InputArgument::OPTIONAL, 'name of new actor class'), - ]); + $this->setDescription('Generates new test suite') + ->addArgument('suite', InputArgument::REQUIRED, 'suite to be generated') + ->addArgument('actor', InputArgument::OPTIONAL, 'name of new actor class'); } - public function getDescription(): string - { - return 'Generates new test suite'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); $suite = ucfirst((string)$input->getArgument('suite')); - $actor = $input->getArgument('actor'); + $config = $this->getGlobalConfig(); + $actor = $input->getArgument('actor') ?: $suite . $config['actor_suffix']; if ($this->containsInvalidCharacters($suite)) { $output->writeln("Suite name '{$suite}' contains invalid characters. ([A-Za-z0-9_])."); - return 1; - } - - $config = $this->getGlobalConfig(); - if (!$actor) { - $actor = $suite . $config['actor_suffix']; + return Command::FAILURE; } $dir = Configuration::testsDir(); @@ -85,26 +75,16 @@ public function execute(InputInterface $input, OutputInterface $output): int # enable helpers as array enabled: [] EOF; - - $this->createFile( - $dir . $suite . '.suite.yml', - $yamlSuiteConfig = (new Template($yamlSuiteConfigTemplate)) - ->place('actor', $actor) - ->place('suite_namespace', $config['namespace'] . '\\' . $suite) - ->produce() - ); - + $yamlSuiteConfig = (new Template($yamlSuiteConfigTemplate)) + ->place('actor', $actor) + ->place('suite_namespace', $config['namespace'] . '\\' . $suite) + ->produce(); + $this->createFile($dir . $suite . '.suite.yml', $yamlSuiteConfig); Configuration::append(Yaml::parse($yamlSuiteConfig)); $actorGenerator = new ActorGenerator(Configuration::config()); $content = $actorGenerator->produce(); - - $file = $this->createDirectoryFor( - Configuration::supportDir(), - $actor - ) . $this->getShortClassName($actor); - $file .= '.php'; - + $file = $this->createDirectoryFor(Configuration::supportDir(), $actor) . $this->getShortClassName($actor) . '.php'; $this->createFile($file, $content); $output->writeln("Actor " . $actor . " was created in {$file}"); @@ -117,7 +97,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $output->writeln("3. Run tests of this suite with codecept run {$suite} command"); $output->writeln("Suite {$suite} generated"); - return 0; + return Command::SUCCESS; } private function containsInvalidCharacters(string $suite): bool diff --git a/src/Codeception/Command/GenerateTest.php b/src/Codeception/Command/GenerateTest.php index 1e9aa61f77..5d1e5a7e05 100644 --- a/src/Codeception/Command/GenerateTest.php +++ b/src/Codeception/Command/GenerateTest.php @@ -23,21 +23,12 @@ class GenerateTest extends Command protected function configure(): void { - $this->setDefinition( - [ - new InputArgument('suite', InputArgument::REQUIRED, 'suite where tests will be put'), - new InputArgument('class', InputArgument::REQUIRED, 'class name'), - ] - ); - parent::configure(); + $this->setDescription('Generates empty unit test file in suite') + ->addArgument('suite', InputArgument::REQUIRED, 'Suite where tests will be put') + ->addArgument('class', InputArgument::REQUIRED, 'Class name'); } - public function getDescription(): string - { - return 'Generates empty unit test file in suite'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $suite = $input->getArgument('suite'); $class = $input->getArgument('class'); @@ -46,19 +37,16 @@ public function execute(InputInterface $input, OutputInterface $output): int $className = $this->getShortClassName($class); $path = $this->createDirectoryFor($config['path'], $class); - - $filename = $this->completeSuffix($className, 'Test'); - $filename = $path . $filename; + $filename = $path . $this->completeSuffix($className, 'Test'); $test = new TestGenerator($config, $class); $res = $this->createFile($filename, $test->produce()); - if (!$res) { $output->writeln("Test {$filename} already exists"); - return 1; + return Command::FAILURE; } $output->writeln("Test was created in {$filename}"); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/GherkinSnippets.php b/src/Codeception/Command/GherkinSnippets.php index cba74dc576..3a1657e42e 100644 --- a/src/Codeception/Command/GherkinSnippets.php +++ b/src/Codeception/Command/GherkinSnippets.php @@ -31,22 +31,13 @@ class GherkinSnippets extends Command protected function configure(): void { - $this->setDefinition( - [ - new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'), - new InputArgument('test', InputArgument::OPTIONAL, 'test to be scanned'), - new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), - ] - ); - parent::configure(); + $this->setDescription('Fetches empty steps from feature files of suite and prints code snippets for them') + ->addArgument('suite', InputArgument::REQUIRED, 'Suite to scan for feature files') + ->addArgument('test', InputArgument::OPTIONAL, 'Test to be scanned') + ->addOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'); } - public function getDescription(): string - { - return 'Fetches empty steps from feature files of suite and prints code snippets for them'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); $suite = $input->getArgument('suite'); @@ -54,16 +45,14 @@ public function execute(InputInterface $input, OutputInterface $output): int $config = $this->getSuiteConfig($suite); $generator = new GherkinSnippetsGenerator($config, $test); - $snippets = $generator->getSnippets(); if ($snippets === []) { $output->writeln(" All Gherkin steps are defined. Exiting... "); - return 0; + return Command::SUCCESS; } $output->writeln(" Snippets found in: "); - $features = $generator->getFeatures(); - foreach ($features as $feature) { + foreach ($generator->getFeatures() as $feature) { $output->writeln(" - {$feature} "); } $output->writeln(" Generated Snippets: "); @@ -74,6 +63,6 @@ public function execute(InputInterface $input, OutputInterface $output): int $output->writeln(" ----------------------------------------- "); $output->writeln(sprintf(' %d snippets proposed', count($snippets))); $output->writeln(" Copy generated snippets to {$config['actor']} or a specific Gherkin context "); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/GherkinSteps.php b/src/Codeception/Command/GherkinSteps.php index 042592ac83..96a77ff56d 100644 --- a/src/Codeception/Command/GherkinSteps.php +++ b/src/Codeception/Command/GherkinSteps.php @@ -29,21 +29,12 @@ class GherkinSteps extends Command protected function configure(): void { - $this->setDefinition( - [ - new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'), - new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), - ] - ); - parent::configure(); + $this->setDescription('Prints all defined feature steps') + ->addArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files') + ->addOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'); } - public function getDescription(): string - { - return 'Prints all defined feature steps'; - } - - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); $suite = $input->getArgument('suite'); @@ -59,11 +50,10 @@ public function execute(InputInterface $input, OutputInterface $output): int $output->writeln("Steps from {$name} context:"); foreach ($context as $step => $callable) { - if (count($callable) < 2) { - continue; + if (count($callable) >= 2) { + $method = $callable[0] . '::' . $callable[1]; + $table->addRow([$step, $method]); } - $method = $callable[0] . '::' . $callable[1]; - $table->addRow([$step, $method]); } $table->render(); } @@ -71,6 +61,6 @@ public function execute(InputInterface $input, OutputInterface $output): int if (!isset($table)) { $output->writeln("No steps are defined, start creating them by running gherkin:snippets"); } - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/Init.php b/src/Codeception/Command/Init.php index ead5326d2c..841ffb14e2 100644 --- a/src/Codeception/Command/Init.php +++ b/src/Codeception/Command/Init.php @@ -19,43 +19,28 @@ class Init extends Command { protected function configure(): void { - $this->setDefinition( - [ - new InputArgument('template', InputArgument::REQUIRED, 'Init template for the setup'), - new InputOption('path', null, InputOption::VALUE_REQUIRED, 'Change current directory', null), - new InputOption('namespace', null, InputOption::VALUE_OPTIONAL, 'Namespace to add for actor classes and helpers', null), - - ] - ); - } - - public function getDescription(): string - { - return "Creates test suites by a template"; + $this->setDescription("Creates test suites by a template") + ->addArgument('template', InputArgument::REQUIRED, 'Init template for the setup') + ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Change current directory') + ->addOption('namespace', null, InputOption::VALUE_OPTIONAL, 'Namespace to add for actor classes and helpers'); } - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { - $template = (string)$input->getArgument('template'); - - if (class_exists($template)) { - $className = $template; - } else { - $className = 'Codeception\Template\\' . ucfirst($template); - - if (!class_exists($className)) { - throw new Exception("Template from a {$className} can't be loaded; Init can't be executed"); - } + $template = (string) $input->getArgument('template'); + $className = class_exists($template) ? $template : 'Codeception\Template\\' . ucfirst($template); + if (!class_exists($className)) { + throw new Exception("Template from a {$className} can't be loaded; Init can't be executed"); } $initProcess = new $className($input, $output); if (!$initProcess instanceof InitTemplate) { - throw new Exception("{$className} is not a valid template"); + throw new Exception($className . ' is not a valid template'); } if ($path = $input->getOption('path')) { $initProcess->initDir($path); } $initProcess->setup(); - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/Run.php b/src/Codeception/Command/Run.php index e87ffd0e68..398c6cdeae 100644 --- a/src/Codeception/Command/Run.php +++ b/src/Codeception/Command/Run.php @@ -147,114 +147,41 @@ class Run extends Command */ protected function configure(): void { - $this->setDefinition([ - new InputArgument('suite', InputArgument::OPTIONAL, 'suite to be tested'), - new InputArgument('test', InputArgument::OPTIONAL, 'test to be run'), - new InputOption('override', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Override config values'), - new InputOption('ext', 'e', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Run with extension enabled'), - new InputOption('report', '', InputOption::VALUE_NONE, 'Show output in compact style'), - new InputOption('html', '', InputOption::VALUE_OPTIONAL, 'Generate html with results', 'report.html'), - new InputOption('xml', '', InputOption::VALUE_OPTIONAL, 'Generate JUnit XML Log', 'report.xml'), - new InputOption('phpunit-xml', '', InputOption::VALUE_OPTIONAL, 'Generate PhpUnit XML Log', 'phpunit-report.xml'), - new InputOption('colors', '', InputOption::VALUE_NONE, 'Use colors in output'), - new InputOption( - 'no-colors', - '', - InputOption::VALUE_NONE, - 'Force no colors in output (useful to override config file)' - ), - new InputOption('silent', '', InputOption::VALUE_NONE, 'Only outputs suite names and final results'), - new InputOption('steps', '', InputOption::VALUE_NONE, 'Show steps in output'), - new InputOption('debug', 'd', InputOption::VALUE_NONE, 'Show debug and scenario output'), - new InputOption('shard', '', InputOption::VALUE_REQUIRED, 'Execute subset of tests to run tests on different machine. To split tests on 3 machines to run with shards: 1/3, 2/3, 3/3'), - new InputOption('filter', '', InputOption::VALUE_REQUIRED, 'Filter tests by name'), - new InputOption('grep', '', InputOption::VALUE_REQUIRED, 'Filter tests by name (alias to --filter)'), - new InputOption('bootstrap', '', InputOption::VALUE_OPTIONAL, 'Execute custom PHP script before running tests. Path can be absolute or relative to current working directory', false), - new InputOption('no-redirect', '', InputOption::VALUE_NONE, 'Do not redirect to Composer-installed version in vendor/codeception'), - new InputOption( - 'coverage', - '', - InputOption::VALUE_OPTIONAL, - 'Run with code coverage' - ), - new InputOption( - 'coverage-html', - '', - InputOption::VALUE_OPTIONAL, - 'Generate CodeCoverage HTML report in path' - ), - new InputOption( - 'coverage-xml', - '', - InputOption::VALUE_OPTIONAL, - 'Generate CodeCoverage XML report in file' - ), - new InputOption( - 'coverage-text', - '', - InputOption::VALUE_OPTIONAL, - 'Generate CodeCoverage text report in file' - ), - new InputOption( - 'coverage-crap4j', - '', - InputOption::VALUE_OPTIONAL, - 'Generate CodeCoverage report in Crap4J XML format' - ), - new InputOption( - 'coverage-cobertura', - '', - InputOption::VALUE_OPTIONAL, - 'Generate CodeCoverage report in Cobertura XML format' - ), - new InputOption( - 'coverage-phpunit', - '', - InputOption::VALUE_OPTIONAL, - 'Generate CodeCoverage PHPUnit report in path' - ), - new InputOption('no-exit', '', InputOption::VALUE_NONE, "Don't finish with exit code"), - new InputOption( - 'group', - 'g', - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Groups of tests to be executed' - ), - new InputOption( - 'skip', - 's', - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Skip selected suites' - ), - new InputOption( - 'skip-group', - 'x', - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Skip selected groups' - ), - new InputOption( - 'env', - '', - InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, - 'Run tests in selected environments.' - ), - new InputOption('fail-fast', 'f', InputOption::VALUE_OPTIONAL, 'Stop after nth failure'), - new InputOption('no-rebuild', '', InputOption::VALUE_NONE, 'Do not rebuild actor classes on start'), - new InputOption( - 'seed', - '', - InputOption::VALUE_REQUIRED, - 'Define random seed for shuffle setting' - ), - new InputOption('no-artifacts', '', InputOption::VALUE_NONE, "Don't report about artifacts"), - ]); - - parent::configure(); - } - - public function getDescription(): string - { - return 'Runs the test suites'; + $this->setDescription('Runs the test suites') + ->addArgument('suite', InputArgument::OPTIONAL, 'suite to be tested') + ->addArgument('test', InputArgument::OPTIONAL, 'test to be run') + ->addOption('override', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Override config values') + ->addOption('ext', 'e', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Run with extension enabled') + ->addOption('report', '', InputOption::VALUE_NONE, 'Show output in compact style') + ->addOption('html', '', InputOption::VALUE_OPTIONAL, 'Generate html with results', 'report.html') + ->addOption('xml', '', InputOption::VALUE_OPTIONAL, 'Generate JUnit XML Log', 'report.xml') + ->addOption('phpunit-xml', '', InputOption::VALUE_OPTIONAL, 'Generate PhpUnit XML Log', 'phpunit-report.xml') + ->addOption('colors', '', InputOption::VALUE_NONE, 'Use colors in output') + ->addOption('no-colors', '', InputOption::VALUE_NONE, 'Force no colors in output (useful to override config file)') + ->addOption('silent', '', InputOption::VALUE_NONE, 'Only outputs suite names and final results') + ->addOption('steps', '', InputOption::VALUE_NONE, 'Show steps in output') + ->addOption('debug', 'd', InputOption::VALUE_NONE, 'Show debug and scenario output') + ->addOption('shard', '', InputOption::VALUE_REQUIRED, 'Execute subset of tests to run tests on different machine. To split tests on 3 machines to run with shards: 1/3, 2/3, 3/3') + ->addOption('filter', '', InputOption::VALUE_REQUIRED, 'Filter tests by name') + ->addOption('grep', '', InputOption::VALUE_REQUIRED, 'Filter tests by name (alias to --filter)') + ->addOption('bootstrap', '', InputOption::VALUE_OPTIONAL, 'Execute custom PHP script before running tests. Path can be absolute or relative to current working directory', false) + ->addOption('no-redirect', '', InputOption::VALUE_NONE, 'Do not redirect to Composer-installed version in vendor/codeception') + ->addOption('coverage', '', InputOption::VALUE_OPTIONAL, 'Run with code coverage') + ->addOption('coverage-html', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage HTML report in path') + ->addOption('coverage-xml', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage XML report in file') + ->addOption('coverage-text', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage text report in file') + ->addOption('coverage-crap4j', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage report in Crap4J XML format') + ->addOption('coverage-cobertura', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage report in Cobertura XML format') + ->addOption('coverage-phpunit', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage PHPUnit report in path') + ->addOption('no-exit', '', InputOption::VALUE_NONE, "Don't finish with exit code") + ->addOption('group', 'g', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Groups of tests to be executed') + ->addOption('skip', 's', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Skip selected suites') + ->addOption('skip-group', 'x', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Skip selected groups') + ->addOption('env', '', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Run tests in selected environments.') + ->addOption('fail-fast', 'f', InputOption::VALUE_OPTIONAL, 'Stop after nth failure') + ->addOption('no-rebuild', '', InputOption::VALUE_NONE, 'Do not rebuild actor classes on start') + ->addOption('seed', '', InputOption::VALUE_REQUIRED, 'Define random seed for shuffle setting') + ->addOption('no-artifacts', '', InputOption::VALUE_NONE, "Don't report about artifacts"); } /** @@ -262,7 +189,7 @@ public function getDescription(): string * * @throws ConfigurationException|ParseException */ - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $this->ensurePhpExtIsAvailable('CURL'); $this->ensurePhpExtIsAvailable('mbstring'); @@ -469,7 +396,7 @@ public function execute(InputInterface $input, OutputInterface $output): int if (!empty($wildcardSuites) && ! empty($appSpecificSuites)) { $this->output->writeLn('Wildcard options can not be combined with specific suites of included apps.'); - return 2; + return Command::INVALID; } if ( @@ -510,7 +437,7 @@ public function execute(InputInterface $input, OutputInterface $output): int exit(1); } - return 0; + return Command::SUCCESS; } protected function matchSingleTest(string $suite, array $config): ?array @@ -580,7 +507,7 @@ protected function runIncludedSuites( string $parentDir, array $filterAppSuites = [], array $filterSuitesByWildcard = [], - ) { + ): void { $defaultConfig = Configuration::config(); $absolutePath = Configuration::projectDir(); diff --git a/src/Codeception/Command/SelfUpdate.php b/src/Codeception/Command/SelfUpdate.php index c31923358a..1f0a82de9b 100644 --- a/src/Codeception/Command/SelfUpdate.php +++ b/src/Codeception/Command/SelfUpdate.php @@ -27,10 +27,12 @@ class SelfUpdate extends Command * @var string */ public const NAME = 'Codeception'; + /** * @var string */ public const GITHUB_REPO = 'Codeception/Codeception'; + /** * @var string */ @@ -41,21 +43,12 @@ class SelfUpdate extends Command */ protected string $filename; - /** - * {@inheritdoc} - */ protected function configure(): void { $this->filename = $_SERVER['argv'][0] ?? Phar::running(false); $this ->setAliases(['selfupdate']) - ->setDescription( - sprintf( - 'Upgrade %s to the latest version', - $this->filename - ) - ); - parent::configure(); + ->setDescription(sprintf('Upgrade %s to the latest version', $this->filename)); } protected function getCurrentVersion(): string @@ -63,48 +56,30 @@ protected function getCurrentVersion(): string return Codecept::VERSION; } - /** - * {@inheritdoc} - */ - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { - $currentVersion = $this->getCurrentVersion(); - $output->writeln( - sprintf( - '%s version %s', - self::NAME, - $currentVersion - ) + sprintf('%s version %s', self::NAME, $this->getCurrentVersion()) ); - $url = self::PHAR_URL; - $updater = new Updater(null, false); - $updater->getStrategy()->setPharUrl($url . 'codecept.phar'); - $updater->getStrategy()->setVersionUrl($url . 'codecept.version'); + $updater->getStrategy()->setPharUrl(self::PHAR_URL . 'codecept.phar'); + $updater->getStrategy()->setVersionUrl(self::PHAR_URL . 'codecept.version'); try { if ($updater->hasUpdate()) { $output->writeln("\nUpdating..."); $updater->update(); - $output->writeln( - sprintf("\n%s has been updated.\n", $this->filename) - ); + $output->writeln("\n{$this->filename} has been updated.\n"); } else { $output->writeln('You are already using the latest version.'); } - } catch (Exception $e) { - $output->writeln( - sprintf( - "\n%s\n", - $e->getMessage() - ) - ); - return 1; + } catch (Exception $exception) { + $output->writeln("\n{$exception->getMessage()}\n"); + return Command::FAILURE; } - return 0; + return Command::SUCCESS; } } diff --git a/src/Codeception/Command/Shared/ConfigTrait.php b/src/Codeception/Command/Shared/ConfigTrait.php index fb8f14e82f..c19bca5a7f 100644 --- a/src/Codeception/Command/Shared/ConfigTrait.php +++ b/src/Codeception/Command/Shared/ConfigTrait.php @@ -34,7 +34,7 @@ protected function getGlobalConfig(string $conf = null): array /** * @return string[] */ - protected function getSuites($conf = null): array + protected function getSuites(): array { return Configuration::suites(); } From 73fd3c9b5c2d5a553bab0dbea3b743d9cd3ad7fd Mon Sep 17 00:00:00 2001 From: Dieter Beck Date: Sun, 28 Jul 2024 02:22:59 +0200 Subject: [PATCH 08/25] Declare nullable parameter types explicitly for PHP 8.4 compatibility (#6774) --- .php-cs-fixer.dist.php | 1 + src/Codeception/Application.php | 2 +- src/Codeception/Codecept.php | 4 ++-- src/Codeception/Command/Shared/ConfigTrait.php | 2 +- src/Codeception/Configuration.php | 2 +- src/Codeception/Exception/Error.php | 2 +- src/Codeception/Exception/ExtensionException.php | 2 +- src/Codeception/Exception/ModuleConfigException.php | 2 +- src/Codeception/Exception/TestParseException.php | 2 +- src/Codeception/InitTemplate.php | 2 +- src/Codeception/Lib/Actor/Shared/Friend.php | 2 +- src/Codeception/Lib/Di.php | 4 ++-- src/Codeception/Lib/ModuleContainer.php | 2 +- src/Codeception/Module.php | 2 +- src/Codeception/Step.php | 2 +- src/Codeception/Step/Comment.php | 2 +- src/Codeception/Step/ConditionalAssertion.php | 2 +- src/Codeception/Step/Executor.php | 2 +- src/Codeception/Step/Incomplete.php | 2 +- src/Codeception/Step/Meta.php | 2 +- src/Codeception/Step/Retry.php | 2 +- src/Codeception/Step/Skip.php | 2 +- src/Codeception/Step/TryTo.php | 2 +- src/Codeception/Subscriber/Console.php | 2 +- src/Codeception/SuiteManager.php | 2 +- src/Codeception/Test/Loader.php | 2 +- src/Codeception/Test/Metadata.php | 2 +- src/Codeception/Util/ReflectionHelper.php | 6 +++--- .../included/jazz/pianist/tests/functional/TestGuy.php | 8 ++++---- tests/data/included/jazz/tests/functional/TestGuy.php | 8 ++++---- tests/data/included/shire/tests/functional/TestGuy.php | 8 ++++---- 31 files changed, 44 insertions(+), 43 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index cec660bde1..0a04cbfc63 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -12,5 +12,6 @@ 'array_syntax' => ['syntax' => 'short'], 'braces' => ['allow_single_line_closure' => true,], 'no_spaces_after_function_name' => true, + 'nullable_type_declaration_for_default_null_value' => true, 'single_blank_line_at_eof' => true, ])->setFinder($finder); diff --git a/src/Codeception/Application.php b/src/Codeception/Application.php index 44cab365f6..546c408de5 100644 --- a/src/Codeception/Application.php +++ b/src/Codeception/Application.php @@ -99,7 +99,7 @@ protected function getCustomCommandName(string $commandClass): string * * @inheritDoc */ - public function run(InputInterface $input = null, OutputInterface $output = null): int + public function run(?InputInterface $input = null, ?OutputInterface $output = null): int { if ($input === null) { $input = $this->getCoreArguments(); diff --git a/src/Codeception/Codecept.php b/src/Codeception/Codecept.php index 1018716693..669d4249ec 100644 --- a/src/Codeception/Codecept.php +++ b/src/Codeception/Codecept.php @@ -200,7 +200,7 @@ private function absolutePath(string $path): string return Configuration::outputDir() . $path; } - public function run(string $suite, string $test = null, array $config = null): void + public function run(string $suite, ?string $test = null, ?array $config = null): void { ini_set( 'memory_limit', @@ -249,7 +249,7 @@ public function run(string $suite, string $test = null, array $config = null): v } } - public function runSuite(array $settings, string $suite, string $test = null): void + public function runSuite(array $settings, string $suite, ?string $test = null): void { $settings['shard'] = $this->options['shard']; $suiteManager = new SuiteManager($this->dispatcher, $suite, $settings, $this->options); diff --git a/src/Codeception/Command/Shared/ConfigTrait.php b/src/Codeception/Command/Shared/ConfigTrait.php index fb8f14e82f..b21aff0d6d 100644 --- a/src/Codeception/Command/Shared/ConfigTrait.php +++ b/src/Codeception/Command/Shared/ConfigTrait.php @@ -26,7 +26,7 @@ protected function getSuiteConfig(string $suite): array return Configuration::suiteSettings($suite, $this->getGlobalConfig()); } - protected function getGlobalConfig(string $conf = null): array + protected function getGlobalConfig(?string $conf = null): array { return Configuration::config($conf); } diff --git a/src/Codeception/Configuration.php b/src/Codeception/Configuration.php index d123d62cfb..8064cf318c 100644 --- a/src/Codeception/Configuration.php +++ b/src/Codeception/Configuration.php @@ -148,7 +148,7 @@ class Configuration * @return array * @throws ConfigurationException */ - public static function config(string $configFile = null): array + public static function config(?string $configFile = null): array { if (!$configFile && self::$config) { return self::$config; diff --git a/src/Codeception/Exception/Error.php b/src/Codeception/Exception/Error.php index bf7b58948a..8e226fc105 100644 --- a/src/Codeception/Exception/Error.php +++ b/src/Codeception/Exception/Error.php @@ -8,7 +8,7 @@ class Error extends Exception { - public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null) + public function __construct(string $message, int $code, string $file, int $line, ?\Exception $previous = null) { parent::__construct($message, $code, $previous); diff --git a/src/Codeception/Exception/ExtensionException.php b/src/Codeception/Exception/ExtensionException.php index c8adace7c6..ddd98112e6 100644 --- a/src/Codeception/Exception/ExtensionException.php +++ b/src/Codeception/Exception/ExtensionException.php @@ -15,7 +15,7 @@ class ExtensionException extends Exception * * @param object|string $extension */ - public function __construct($extension, string $message, Exception $previous = null) + public function __construct($extension, string $message, ?Exception $previous = null) { parent::__construct($message, 0, $previous); if (is_object($extension)) { diff --git a/src/Codeception/Exception/ModuleConfigException.php b/src/Codeception/Exception/ModuleConfigException.php index 315cdf8f11..5a7a4ae1fb 100644 --- a/src/Codeception/Exception/ModuleConfigException.php +++ b/src/Codeception/Exception/ModuleConfigException.php @@ -17,7 +17,7 @@ class ModuleConfigException extends Exception * * @param object|string $module */ - public function __construct($module, string $message, Exception $previous = null) + public function __construct($module, string $message, ?Exception $previous = null) { if (is_object($module)) { $module = $module::class; diff --git a/src/Codeception/Exception/TestParseException.php b/src/Codeception/Exception/TestParseException.php index 4e2df4cb13..04ae1a775b 100644 --- a/src/Codeception/Exception/TestParseException.php +++ b/src/Codeception/Exception/TestParseException.php @@ -8,7 +8,7 @@ class TestParseException extends Exception { - public function __construct(string $fileName, string $errors = null, int $line = null) + public function __construct(string $fileName, ?string $errors = null, ?int $line = null) { $this->message = "Couldn't parse test '{$fileName}'"; if ($line !== null) { diff --git a/src/Codeception/InitTemplate.php b/src/Codeception/InitTemplate.php index 797a42f6e9..f9e50135f6 100644 --- a/src/Codeception/InitTemplate.php +++ b/src/Codeception/InitTemplate.php @@ -97,7 +97,7 @@ abstract public function setup(); * * @return mixed|string */ - protected function ask(string $question, string|bool|array $answer = null): mixed + protected function ask(string $question, string|bool|array|null $answer = null): mixed { $question = "? {$question}"; $dialog = new QuestionHelper(); diff --git a/src/Codeception/Lib/Actor/Shared/Friend.php b/src/Codeception/Lib/Actor/Shared/Friend.php index 7961f14d20..50f90ffd1d 100644 --- a/src/Codeception/Lib/Actor/Shared/Friend.php +++ b/src/Codeception/Lib/Actor/Shared/Friend.php @@ -13,7 +13,7 @@ trait Friend abstract protected function getScenario(): Scenario; - public function haveFriend(string $name, string $actorClass = null): LibFriend + public function haveFriend(string $name, ?string $actorClass = null): LibFriend { if (!isset($this->friends[$name])) { $actor = $actorClass === null ? $this : new $actorClass($this->getScenario()); diff --git a/src/Codeception/Lib/Di.php b/src/Codeception/Lib/Di.php index 5a6f6e370d..24413ee75f 100644 --- a/src/Codeception/Lib/Di.php +++ b/src/Codeception/Lib/Di.php @@ -26,7 +26,7 @@ class Di protected ?Di $fallback = null; - public function __construct(Di $fallback = null) + public function __construct(?Di $fallback = null) { $this->fallback = $fallback; } @@ -50,7 +50,7 @@ public function set(object $class): void */ public function instantiate( string $className, - array $constructorArgs = null, + ?array $constructorArgs = null, string $injectMethodName = self::DEFAULT_INJECT_METHOD_NAME ): ?object { // normalize namespace diff --git a/src/Codeception/Lib/ModuleContainer.php b/src/Codeception/Lib/ModuleContainer.php index bda5655dc0..163f3dc31f 100644 --- a/src/Codeception/Lib/ModuleContainer.php +++ b/src/Codeception/Lib/ModuleContainer.php @@ -171,7 +171,7 @@ private function getActionsForModule(Module $module, array $config): array /** * Should a method be included as an action? */ - private function includeMethodAsAction(Module $module, ReflectionMethod $method, array $configuredParts = null): bool + private function includeMethodAsAction(Module $module, ReflectionMethod $method, ?array $configuredParts = null): bool { // Filter out excluded actions if ($module::$excludeActions && in_array($method->name, $module::$excludeActions)) { diff --git a/src/Codeception/Module.php b/src/Codeception/Module.php index ebf1db81ab..72bb9999f5 100644 --- a/src/Codeception/Module.php +++ b/src/Codeception/Module.php @@ -298,7 +298,7 @@ protected function getModule(string $name): Module * @param string|null $key * @return mixed the config item's value or null if it doesn't exist */ - public function _getConfig(string $key = null): mixed + public function _getConfig(?string $key = null): mixed { if (!$key) { return $this->config; diff --git a/src/Codeception/Step.php b/src/Codeception/Step.php index fa28ceb09d..26b643c192 100644 --- a/src/Codeception/Step.php +++ b/src/Codeception/Step.php @@ -276,7 +276,7 @@ protected function humanize(string $text): string /** * @return mixed */ - public function run(ModuleContainer $container = null) + public function run(?ModuleContainer $container = null) { $this->executed = true; if ($container === null) { diff --git a/src/Codeception/Step/Comment.php b/src/Codeception/Step/Comment.php index 8162eb1d03..ef5943a5c2 100644 --- a/src/Codeception/Step/Comment.php +++ b/src/Codeception/Step/Comment.php @@ -31,7 +31,7 @@ public function getPhpCode(int $maxLength): string return '// ' . $this->getAction(); } - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { // don't do anything, let's rest } diff --git a/src/Codeception/Step/ConditionalAssertion.php b/src/Codeception/Step/ConditionalAssertion.php index d7141bdd25..015b48b19b 100644 --- a/src/Codeception/Step/ConditionalAssertion.php +++ b/src/Codeception/Step/ConditionalAssertion.php @@ -15,7 +15,7 @@ class ConditionalAssertion extends Assertion implements GeneratedStep { - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { try { parent::run($container); diff --git a/src/Codeception/Step/Executor.php b/src/Codeception/Step/Executor.php index 122e70ff53..da5cd0d70d 100644 --- a/src/Codeception/Step/Executor.php +++ b/src/Codeception/Step/Executor.php @@ -19,7 +19,7 @@ public function __construct(Closure $callable, array $arguments = []) $this->callable = $callable; } - public function run(ModuleContainer $container = null) + public function run(?ModuleContainer $container = null) { $callable = $this->callable; diff --git a/src/Codeception/Step/Incomplete.php b/src/Codeception/Step/Incomplete.php index 1ee849b2e8..022579c47f 100644 --- a/src/Codeception/Step/Incomplete.php +++ b/src/Codeception/Step/Incomplete.php @@ -10,7 +10,7 @@ class Incomplete extends CodeceptionStep { - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { throw new IncompleteTestError($this->getAction()); } diff --git a/src/Codeception/Step/Meta.php b/src/Codeception/Step/Meta.php index 3711754864..f3eb9baa1a 100644 --- a/src/Codeception/Step/Meta.php +++ b/src/Codeception/Step/Meta.php @@ -14,7 +14,7 @@ class Meta extends CodeceptionStep { - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { } diff --git a/src/Codeception/Step/Retry.php b/src/Codeception/Step/Retry.php index 1198f3b6fe..e39fb98377 100644 --- a/src/Codeception/Step/Retry.php +++ b/src/Codeception/Step/Retry.php @@ -38,7 +38,7 @@ public function __construct($action, array $arguments, private int $retryNum, pr $this->arguments = $arguments; } - public function run(ModuleContainer $container = null) + public function run(?ModuleContainer $container = null) { $retry = 0; $interval = $this->retryInterval; diff --git a/src/Codeception/Step/Skip.php b/src/Codeception/Step/Skip.php index 064ea004f7..5333abd575 100644 --- a/src/Codeception/Step/Skip.php +++ b/src/Codeception/Step/Skip.php @@ -12,7 +12,7 @@ class Skip extends CodeceptionStep { - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { $skipMessage = $this->getAction(); diff --git a/src/Codeception/Step/TryTo.php b/src/Codeception/Step/TryTo.php index cca9eea2b2..a90635724e 100644 --- a/src/Codeception/Step/TryTo.php +++ b/src/Codeception/Step/TryTo.php @@ -13,7 +13,7 @@ class TryTo extends Assertion implements GeneratedStep { - public function run(ModuleContainer $container = null): bool + public function run(?ModuleContainer $container = null): bool { $this->isTry = true; try { diff --git a/src/Codeception/Subscriber/Console.php b/src/Codeception/Subscriber/Console.php index e20494fe7b..8bf0ac1bb2 100644 --- a/src/Codeception/Subscriber/Console.php +++ b/src/Codeception/Subscriber/Console.php @@ -542,7 +542,7 @@ public function printReports(TestInterface $failedTest): void } } - public function printException($exception, string $cause = null): void + public function printException($exception, ?string $cause = null): void { if ($exception instanceof SkippedTest || $exception instanceof IncompleteTestError) { if ($exception->getMessage() !== '') { diff --git a/src/Codeception/SuiteManager.php b/src/Codeception/SuiteManager.php index ae229a81bb..0d30803ecc 100644 --- a/src/Codeception/SuiteManager.php +++ b/src/Codeception/SuiteManager.php @@ -81,7 +81,7 @@ public function initialize(): void ini_set('xdebug.show_exception_trace', '0'); // Issue https://github.com/symfony/symfony/issues/7646 } - public function loadTests(string $path = null): void + public function loadTests(?string $path = null): void { $testLoader = new Loader($this->settings); $testLoader->loadTests($path); diff --git a/src/Codeception/Test/Loader.php b/src/Codeception/Test/Loader.php index 9391d2e3f5..493e926240 100644 --- a/src/Codeception/Test/Loader.php +++ b/src/Codeception/Test/Loader.php @@ -170,7 +170,7 @@ public function loadTest(string $path): void throw new Exception('Test format not supported. Please, check you use the right suffix. Available filetypes: Cept, Cest, Test'); } - public function loadTests(string $fileName = null): void + public function loadTests(?string $fileName = null): void { if ($fileName) { $this->loadTest($fileName); diff --git a/src/Codeception/Test/Metadata.php b/src/Codeception/Test/Metadata.php index ef4e2961be..042b093194 100644 --- a/src/Codeception/Test/Metadata.php +++ b/src/Codeception/Test/Metadata.php @@ -181,7 +181,7 @@ public function addReport(string $type, $report): void * Returns test params like: env, group, skip, incomplete, etc. * Can return by annotation or return all if no key passed */ - public function getParam(string $key = null): mixed + public function getParam(?string $key = null): mixed { if ($key) { if (isset($this->params[$key])) { diff --git a/src/Codeception/Util/ReflectionHelper.php b/src/Codeception/Util/ReflectionHelper.php index 501fbd4917..d4f3ff4582 100644 --- a/src/Codeception/Util/ReflectionHelper.php +++ b/src/Codeception/Util/ReflectionHelper.php @@ -37,7 +37,7 @@ class ReflectionHelper * * @throws ReflectionException */ - public static function readPrivateProperty(object $object, string $property, string $class = null): mixed + public static function readPrivateProperty(object $object, string $property, ?string $class = null): mixed { if (is_null($class)) { $class = $object; @@ -54,7 +54,7 @@ public static function readPrivateProperty(object $object, string $property, str * * @throws ReflectionException */ - public static function setPrivateProperty(object $object, string $property, $value, string $class = null): void + public static function setPrivateProperty(object $object, string $property, $value, ?string $class = null): void { if (is_null($class)) { $class = $object; @@ -71,7 +71,7 @@ public static function setPrivateProperty(object $object, string $property, $val * * @throws ReflectionException */ - public static function invokePrivateMethod(?object $object, string $method, array $args = [], string $class = null): mixed + public static function invokePrivateMethod(?object $object, string $method, array $args = [], ?string $class = null): mixed { if (is_null($class)) { $class = $object; diff --git a/tests/data/included/jazz/pianist/tests/functional/TestGuy.php b/tests/data/included/jazz/pianist/tests/functional/TestGuy.php index df9747de77..00d80e8180 100644 --- a/tests/data/included/jazz/pianist/tests/functional/TestGuy.php +++ b/tests/data/included/jazz/pianist/tests/functional/TestGuy.php @@ -291,7 +291,7 @@ public function deleteThisFile() * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function canSeeFileFound($filename, string $path = null) + public function canSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); } @@ -312,7 +312,7 @@ public function canSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function seeFileFound($filename, string $path = null) + public function seeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); } @@ -328,7 +328,7 @@ public function seeFileFound($filename, string $path = null) * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function cantSeeFileFound($filename, string $path = null) + public function cantSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); } @@ -342,7 +342,7 @@ public function cantSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function dontSeeFileFound($filename, string $path = null) + public function dontSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); } diff --git a/tests/data/included/jazz/tests/functional/TestGuy.php b/tests/data/included/jazz/tests/functional/TestGuy.php index 5f67138bf4..c2663c4879 100644 --- a/tests/data/included/jazz/tests/functional/TestGuy.php +++ b/tests/data/included/jazz/tests/functional/TestGuy.php @@ -291,7 +291,7 @@ public function deleteThisFile() * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function canSeeFileFound($filename, string $path = null) + public function canSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); } @@ -312,7 +312,7 @@ public function canSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function seeFileFound($filename, string $path = null) + public function seeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); } @@ -328,7 +328,7 @@ public function seeFileFound($filename, string $path = null) * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function cantSeeFileFound($filename, string $path = null) + public function cantSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); } @@ -342,7 +342,7 @@ public function cantSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function dontSeeFileFound($filename, string $path = null) + public function dontSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); } diff --git a/tests/data/included/shire/tests/functional/TestGuy.php b/tests/data/included/shire/tests/functional/TestGuy.php index 92bb2c9084..23458114b2 100644 --- a/tests/data/included/shire/tests/functional/TestGuy.php +++ b/tests/data/included/shire/tests/functional/TestGuy.php @@ -291,7 +291,7 @@ public function deleteThisFile() * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function canSeeFileFound($filename, string $path = null) + public function canSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); } @@ -312,7 +312,7 @@ public function canSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function seeFileFound($filename, string $path = null) + public function seeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); } @@ -328,7 +328,7 @@ public function seeFileFound($filename, string $path = null) * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function cantSeeFileFound($filename, string $path = null) + public function cantSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); } @@ -342,7 +342,7 @@ public function cantSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function dontSeeFileFound($filename, string $path = null) + public function dontSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); } From eb0634c6215abe3854d25c09ef74a3285a062667 Mon Sep 17 00:00:00 2001 From: Dieter Beck Date: Sun, 28 Jul 2024 02:23:14 +0200 Subject: [PATCH 09/25] Declare nullable parameter types explicitly (#6775) Implicit nullable types are deprecated in PHP 8.4 --- .php-cs-fixer.dist.php | 1 + src/Codeception/Application.php | 2 +- src/Codeception/Codecept.php | 4 ++-- src/Codeception/Command/Shared/ConfigTrait.php | 2 +- src/Codeception/Configuration.php | 2 +- src/Codeception/Exception/Error.php | 2 +- src/Codeception/Exception/ExtensionException.php | 2 +- src/Codeception/Exception/ModuleConfigException.php | 2 +- src/Codeception/Exception/TestParseException.php | 2 +- src/Codeception/InitTemplate.php | 2 +- src/Codeception/Lib/Actor/Shared/Friend.php | 2 +- src/Codeception/Lib/Di.php | 4 ++-- src/Codeception/Lib/ModuleContainer.php | 2 +- src/Codeception/Module.php | 2 +- src/Codeception/Step.php | 2 +- src/Codeception/Step/Comment.php | 2 +- src/Codeception/Step/ConditionalAssertion.php | 2 +- src/Codeception/Step/Executor.php | 2 +- src/Codeception/Step/Incomplete.php | 2 +- src/Codeception/Step/Meta.php | 2 +- src/Codeception/Step/Retry.php | 2 +- src/Codeception/Step/Skip.php | 2 +- src/Codeception/Step/TryTo.php | 2 +- src/Codeception/Subscriber/Console.php | 2 +- src/Codeception/SuiteManager.php | 2 +- src/Codeception/Test/Loader.php | 2 +- src/Codeception/Test/Metadata.php | 2 +- src/Codeception/Util/ReflectionHelper.php | 6 +++--- .../included/jazz/pianist/tests/functional/TestGuy.php | 8 ++++---- tests/data/included/jazz/tests/functional/TestGuy.php | 8 ++++---- tests/data/included/shire/tests/functional/TestGuy.php | 8 ++++---- 31 files changed, 44 insertions(+), 43 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index cec660bde1..0a04cbfc63 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -12,5 +12,6 @@ 'array_syntax' => ['syntax' => 'short'], 'braces' => ['allow_single_line_closure' => true,], 'no_spaces_after_function_name' => true, + 'nullable_type_declaration_for_default_null_value' => true, 'single_blank_line_at_eof' => true, ])->setFinder($finder); diff --git a/src/Codeception/Application.php b/src/Codeception/Application.php index c275921669..3244c44bfe 100644 --- a/src/Codeception/Application.php +++ b/src/Codeception/Application.php @@ -99,7 +99,7 @@ protected function getCustomCommandName(string $commandClass): string * * @inheritDoc */ - public function run(InputInterface $input = null, OutputInterface $output = null): int + public function run(?InputInterface $input = null, ?OutputInterface $output = null): int { if (!$input instanceof InputInterface) { $input = $this->getCoreArguments(); diff --git a/src/Codeception/Codecept.php b/src/Codeception/Codecept.php index ad8c52916d..0689f02c3c 100644 --- a/src/Codeception/Codecept.php +++ b/src/Codeception/Codecept.php @@ -192,7 +192,7 @@ private function registerReporters(): void } } - public function run(string $suite, string $test = null, array $config = null): void + public function run(string $suite, ?string $test = null, ?array $config = null): void { ini_set( 'memory_limit', @@ -241,7 +241,7 @@ public function run(string $suite, string $test = null, array $config = null): v } } - public function runSuite(array $settings, string $suite, string $test = null): void + public function runSuite(array $settings, string $suite, ?string $test = null): void { $settings['shard'] = $this->options['shard']; $suiteManager = new SuiteManager($this->dispatcher, $suite, $settings, $this->options); diff --git a/src/Codeception/Command/Shared/ConfigTrait.php b/src/Codeception/Command/Shared/ConfigTrait.php index c19bca5a7f..2eed6c8823 100644 --- a/src/Codeception/Command/Shared/ConfigTrait.php +++ b/src/Codeception/Command/Shared/ConfigTrait.php @@ -26,7 +26,7 @@ protected function getSuiteConfig(string $suite): array return Configuration::suiteSettings($suite, $this->getGlobalConfig()); } - protected function getGlobalConfig(string $conf = null): array + protected function getGlobalConfig(?string $conf = null): array { return Configuration::config($conf); } diff --git a/src/Codeception/Configuration.php b/src/Codeception/Configuration.php index 15c99b3f3e..972360c194 100644 --- a/src/Codeception/Configuration.php +++ b/src/Codeception/Configuration.php @@ -148,7 +148,7 @@ class Configuration * @return array * @throws ConfigurationException */ - public static function config(string $configFile = null): array + public static function config(?string $configFile = null): array { if (!$configFile && self::$config) { return self::$config; diff --git a/src/Codeception/Exception/Error.php b/src/Codeception/Exception/Error.php index bf7b58948a..8e226fc105 100644 --- a/src/Codeception/Exception/Error.php +++ b/src/Codeception/Exception/Error.php @@ -8,7 +8,7 @@ class Error extends Exception { - public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null) + public function __construct(string $message, int $code, string $file, int $line, ?\Exception $previous = null) { parent::__construct($message, $code, $previous); diff --git a/src/Codeception/Exception/ExtensionException.php b/src/Codeception/Exception/ExtensionException.php index c8adace7c6..ddd98112e6 100644 --- a/src/Codeception/Exception/ExtensionException.php +++ b/src/Codeception/Exception/ExtensionException.php @@ -15,7 +15,7 @@ class ExtensionException extends Exception * * @param object|string $extension */ - public function __construct($extension, string $message, Exception $previous = null) + public function __construct($extension, string $message, ?Exception $previous = null) { parent::__construct($message, 0, $previous); if (is_object($extension)) { diff --git a/src/Codeception/Exception/ModuleConfigException.php b/src/Codeception/Exception/ModuleConfigException.php index 315cdf8f11..5a7a4ae1fb 100644 --- a/src/Codeception/Exception/ModuleConfigException.php +++ b/src/Codeception/Exception/ModuleConfigException.php @@ -17,7 +17,7 @@ class ModuleConfigException extends Exception * * @param object|string $module */ - public function __construct($module, string $message, Exception $previous = null) + public function __construct($module, string $message, ?Exception $previous = null) { if (is_object($module)) { $module = $module::class; diff --git a/src/Codeception/Exception/TestParseException.php b/src/Codeception/Exception/TestParseException.php index 4e2df4cb13..04ae1a775b 100644 --- a/src/Codeception/Exception/TestParseException.php +++ b/src/Codeception/Exception/TestParseException.php @@ -8,7 +8,7 @@ class TestParseException extends Exception { - public function __construct(string $fileName, string $errors = null, int $line = null) + public function __construct(string $fileName, ?string $errors = null, ?int $line = null) { $this->message = "Couldn't parse test '{$fileName}'"; if ($line !== null) { diff --git a/src/Codeception/InitTemplate.php b/src/Codeception/InitTemplate.php index 797a42f6e9..f9e50135f6 100644 --- a/src/Codeception/InitTemplate.php +++ b/src/Codeception/InitTemplate.php @@ -97,7 +97,7 @@ abstract public function setup(); * * @return mixed|string */ - protected function ask(string $question, string|bool|array $answer = null): mixed + protected function ask(string $question, string|bool|array|null $answer = null): mixed { $question = "? {$question}"; $dialog = new QuestionHelper(); diff --git a/src/Codeception/Lib/Actor/Shared/Friend.php b/src/Codeception/Lib/Actor/Shared/Friend.php index 7961f14d20..50f90ffd1d 100644 --- a/src/Codeception/Lib/Actor/Shared/Friend.php +++ b/src/Codeception/Lib/Actor/Shared/Friend.php @@ -13,7 +13,7 @@ trait Friend abstract protected function getScenario(): Scenario; - public function haveFriend(string $name, string $actorClass = null): LibFriend + public function haveFriend(string $name, ?string $actorClass = null): LibFriend { if (!isset($this->friends[$name])) { $actor = $actorClass === null ? $this : new $actorClass($this->getScenario()); diff --git a/src/Codeception/Lib/Di.php b/src/Codeception/Lib/Di.php index 35b0a60809..ad1e6c09ef 100644 --- a/src/Codeception/Lib/Di.php +++ b/src/Codeception/Lib/Di.php @@ -27,7 +27,7 @@ class Di protected ?Di $fallback = null; - public function __construct(Di $fallback = null) + public function __construct(?Di $fallback = null) { $this->fallback = $fallback; } @@ -51,7 +51,7 @@ public function set(object $class): void */ public function instantiate( string $className, - array $constructorArgs = null, + ?array $constructorArgs = null, string $injectMethodName = self::DEFAULT_INJECT_METHOD_NAME ): ?object { // normalize namespace diff --git a/src/Codeception/Lib/ModuleContainer.php b/src/Codeception/Lib/ModuleContainer.php index 02cde00a9c..7868f930fd 100644 --- a/src/Codeception/Lib/ModuleContainer.php +++ b/src/Codeception/Lib/ModuleContainer.php @@ -171,7 +171,7 @@ private function getActionsForModule(Module $module, array $config): array /** * Should a method be included as an action? */ - private function includeMethodAsAction(Module $module, ReflectionMethod $method, array $configuredParts = null): bool + private function includeMethodAsAction(Module $module, ReflectionMethod $method, ?array $configuredParts = null): bool { // Filter out excluded actions if ($module::$excludeActions && in_array($method->name, $module::$excludeActions)) { diff --git a/src/Codeception/Module.php b/src/Codeception/Module.php index 6702ce722d..559011074f 100644 --- a/src/Codeception/Module.php +++ b/src/Codeception/Module.php @@ -298,7 +298,7 @@ protected function getModule(string $name): Module * @param string|null $key * @return mixed the config item's value or null if it doesn't exist */ - public function _getConfig(string $key = null): mixed + public function _getConfig(?string $key = null): mixed { if (!$key) { return $this->config; diff --git a/src/Codeception/Step.php b/src/Codeception/Step.php index 38643ce662..ce032a701d 100644 --- a/src/Codeception/Step.php +++ b/src/Codeception/Step.php @@ -274,7 +274,7 @@ protected function humanize(string $text): string /** * @return mixed */ - public function run(ModuleContainer $container = null) + public function run(?ModuleContainer $container = null) { $this->executed = true; if (!$container instanceof ModuleContainer) { diff --git a/src/Codeception/Step/Comment.php b/src/Codeception/Step/Comment.php index 8162eb1d03..ef5943a5c2 100644 --- a/src/Codeception/Step/Comment.php +++ b/src/Codeception/Step/Comment.php @@ -31,7 +31,7 @@ public function getPhpCode(int $maxLength): string return '// ' . $this->getAction(); } - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { // don't do anything, let's rest } diff --git a/src/Codeception/Step/ConditionalAssertion.php b/src/Codeception/Step/ConditionalAssertion.php index d7141bdd25..015b48b19b 100644 --- a/src/Codeception/Step/ConditionalAssertion.php +++ b/src/Codeception/Step/ConditionalAssertion.php @@ -15,7 +15,7 @@ class ConditionalAssertion extends Assertion implements GeneratedStep { - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { try { parent::run($container); diff --git a/src/Codeception/Step/Executor.php b/src/Codeception/Step/Executor.php index 73a5624b1d..51427a032d 100644 --- a/src/Codeception/Step/Executor.php +++ b/src/Codeception/Step/Executor.php @@ -15,7 +15,7 @@ public function __construct(protected Closure $callable, array $arguments = []) parent::__construct('execute callable function', []); } - public function run(ModuleContainer $container = null) + public function run(?ModuleContainer $container = null) { $callable = $this->callable; diff --git a/src/Codeception/Step/Incomplete.php b/src/Codeception/Step/Incomplete.php index 1ee849b2e8..022579c47f 100644 --- a/src/Codeception/Step/Incomplete.php +++ b/src/Codeception/Step/Incomplete.php @@ -10,7 +10,7 @@ class Incomplete extends CodeceptionStep { - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { throw new IncompleteTestError($this->getAction()); } diff --git a/src/Codeception/Step/Meta.php b/src/Codeception/Step/Meta.php index 3711754864..f3eb9baa1a 100644 --- a/src/Codeception/Step/Meta.php +++ b/src/Codeception/Step/Meta.php @@ -14,7 +14,7 @@ class Meta extends CodeceptionStep { - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { } diff --git a/src/Codeception/Step/Retry.php b/src/Codeception/Step/Retry.php index 1198f3b6fe..e39fb98377 100644 --- a/src/Codeception/Step/Retry.php +++ b/src/Codeception/Step/Retry.php @@ -38,7 +38,7 @@ public function __construct($action, array $arguments, private int $retryNum, pr $this->arguments = $arguments; } - public function run(ModuleContainer $container = null) + public function run(?ModuleContainer $container = null) { $retry = 0; $interval = $this->retryInterval; diff --git a/src/Codeception/Step/Skip.php b/src/Codeception/Step/Skip.php index 064ea004f7..5333abd575 100644 --- a/src/Codeception/Step/Skip.php +++ b/src/Codeception/Step/Skip.php @@ -12,7 +12,7 @@ class Skip extends CodeceptionStep { - public function run(ModuleContainer $container = null): void + public function run(?ModuleContainer $container = null): void { $skipMessage = $this->getAction(); diff --git a/src/Codeception/Step/TryTo.php b/src/Codeception/Step/TryTo.php index cca9eea2b2..a90635724e 100644 --- a/src/Codeception/Step/TryTo.php +++ b/src/Codeception/Step/TryTo.php @@ -13,7 +13,7 @@ class TryTo extends Assertion implements GeneratedStep { - public function run(ModuleContainer $container = null): bool + public function run(?ModuleContainer $container = null): bool { $this->isTry = true; try { diff --git a/src/Codeception/Subscriber/Console.php b/src/Codeception/Subscriber/Console.php index 254447b2e6..1364418573 100644 --- a/src/Codeception/Subscriber/Console.php +++ b/src/Codeception/Subscriber/Console.php @@ -541,7 +541,7 @@ public function printReports(TestInterface $failedTest): void } } - public function printException($exception, string $cause = null): void + public function printException($exception, ?string $cause = null): void { if ($exception instanceof SkippedTest || $exception instanceof IncompleteTestError) { if ($exception->getMessage() !== '') { diff --git a/src/Codeception/SuiteManager.php b/src/Codeception/SuiteManager.php index 4b19a15f7b..b57667c23e 100644 --- a/src/Codeception/SuiteManager.php +++ b/src/Codeception/SuiteManager.php @@ -78,7 +78,7 @@ public function initialize(): void ini_set('xdebug.show_exception_trace', '0'); // Issue https://github.com/symfony/symfony/issues/7646 } - public function loadTests(string $path = null): void + public function loadTests(?string $path = null): void { $testLoader = new Loader($this->settings); $testLoader->loadTests($path); diff --git a/src/Codeception/Test/Loader.php b/src/Codeception/Test/Loader.php index 421572d0b8..88f38acb09 100644 --- a/src/Codeception/Test/Loader.php +++ b/src/Codeception/Test/Loader.php @@ -170,7 +170,7 @@ public function loadTest(string $path): void throw new Exception('Test format not supported. Please, check you use the right suffix. Available filetypes: Cept, Cest, Test'); } - public function loadTests(string $fileName = null): void + public function loadTests(?string $fileName = null): void { if ($fileName) { $this->loadTest($fileName); diff --git a/src/Codeception/Test/Metadata.php b/src/Codeception/Test/Metadata.php index 25c61c9c25..9e588ad4c3 100644 --- a/src/Codeception/Test/Metadata.php +++ b/src/Codeception/Test/Metadata.php @@ -185,7 +185,7 @@ public function addReport(string $type, $report): void * Returns test params like: env, group, skip, incomplete, etc. * Can return by annotation or return all if no key passed */ - public function getParam(string $key = null): mixed + public function getParam(?string $key = null): mixed { if ($key) { if (isset($this->params[$key])) { diff --git a/src/Codeception/Util/ReflectionHelper.php b/src/Codeception/Util/ReflectionHelper.php index 44096e62dd..54f405cd08 100644 --- a/src/Codeception/Util/ReflectionHelper.php +++ b/src/Codeception/Util/ReflectionHelper.php @@ -36,7 +36,7 @@ class ReflectionHelper * * @throws ReflectionException */ - public static function readPrivateProperty(object $object, string $property, string $class = null): mixed + public static function readPrivateProperty(object $object, string $property, ?string $class = null): mixed { if (is_null($class)) { $class = $object; @@ -53,7 +53,7 @@ public static function readPrivateProperty(object $object, string $property, str * * @throws ReflectionException */ - public static function setPrivateProperty(object $object, string $property, $value, string $class = null): void + public static function setPrivateProperty(object $object, string $property, $value, ?string $class = null): void { if (is_null($class)) { $class = $object; @@ -70,7 +70,7 @@ public static function setPrivateProperty(object $object, string $property, $val * * @throws ReflectionException */ - public static function invokePrivateMethod(?object $object, string $method, array $args = [], string $class = null): mixed + public static function invokePrivateMethod(?object $object, string $method, array $args = [], ?string $class = null): mixed { if (is_null($class)) { $class = $object; diff --git a/tests/data/included/jazz/pianist/tests/functional/TestGuy.php b/tests/data/included/jazz/pianist/tests/functional/TestGuy.php index df9747de77..00d80e8180 100644 --- a/tests/data/included/jazz/pianist/tests/functional/TestGuy.php +++ b/tests/data/included/jazz/pianist/tests/functional/TestGuy.php @@ -291,7 +291,7 @@ public function deleteThisFile() * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function canSeeFileFound($filename, string $path = null) + public function canSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); } @@ -312,7 +312,7 @@ public function canSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function seeFileFound($filename, string $path = null) + public function seeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); } @@ -328,7 +328,7 @@ public function seeFileFound($filename, string $path = null) * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function cantSeeFileFound($filename, string $path = null) + public function cantSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); } @@ -342,7 +342,7 @@ public function cantSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function dontSeeFileFound($filename, string $path = null) + public function dontSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); } diff --git a/tests/data/included/jazz/tests/functional/TestGuy.php b/tests/data/included/jazz/tests/functional/TestGuy.php index 5f67138bf4..c2663c4879 100644 --- a/tests/data/included/jazz/tests/functional/TestGuy.php +++ b/tests/data/included/jazz/tests/functional/TestGuy.php @@ -291,7 +291,7 @@ public function deleteThisFile() * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function canSeeFileFound($filename, string $path = null) + public function canSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); } @@ -312,7 +312,7 @@ public function canSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function seeFileFound($filename, string $path = null) + public function seeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); } @@ -328,7 +328,7 @@ public function seeFileFound($filename, string $path = null) * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function cantSeeFileFound($filename, string $path = null) + public function cantSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); } @@ -342,7 +342,7 @@ public function cantSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function dontSeeFileFound($filename, string $path = null) + public function dontSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); } diff --git a/tests/data/included/shire/tests/functional/TestGuy.php b/tests/data/included/shire/tests/functional/TestGuy.php index 92bb2c9084..23458114b2 100644 --- a/tests/data/included/shire/tests/functional/TestGuy.php +++ b/tests/data/included/shire/tests/functional/TestGuy.php @@ -291,7 +291,7 @@ public function deleteThisFile() * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function canSeeFileFound($filename, string $path = null) + public function canSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); } @@ -312,7 +312,7 @@ public function canSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::seeFileFound() */ - public function seeFileFound($filename, string $path = null) + public function seeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); } @@ -328,7 +328,7 @@ public function seeFileFound($filename, string $path = null) * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function cantSeeFileFound($filename, string $path = null) + public function cantSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args())); } @@ -342,7 +342,7 @@ public function cantSeeFileFound($filename, string $path = null) * @param string|null $path * @see \Codeception\Module\Filesystem::dontSeeFileFound() */ - public function dontSeeFileFound($filename, string $path = null) + public function dontSeeFileFound($filename, ?string $path = null) { return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args())); } From 2b7af3f8b5722e9d6eda88834588cb0581196372 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 11 Sep 2024 15:43:18 +0200 Subject: [PATCH 10/25] chore: Included githubactions in the dependabot config (#6471) (#6783) This should help with keeping the GitHub actions updated on new releases. This will also help with keeping it secure. Dependabot helps in keeping the supply chain secure https://docs.github.com/en/code-security/dependabot GitHub actions up to date https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot https://github.com/ossf/scorecard/blob/main/docs/checks.md#dependency-update-tool Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> Co-authored-by: Naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..5ace4600a1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From a95de4561090d78fb9e7c641216597953be4f79d Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Wed, 11 Sep 2024 15:43:18 +0200 Subject: [PATCH 11/25] chore: Included githubactions in the dependabot config (#6471) (#6783) This should help with keeping the GitHub actions updated on new releases. This will also help with keeping it secure. Dependabot helps in keeping the supply chain secure https://docs.github.com/en/code-security/dependabot GitHub actions up to date https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot https://github.com/ossf/scorecard/blob/main/docs/checks.md#dependency-update-tool Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> Co-authored-by: Naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..5ace4600a1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From d80057cd52e7d415442aab08e56ba64fe60f5f7e Mon Sep 17 00:00:00 2001 From: Viktor Linkin Date: Fri, 13 Sep 2024 12:09:49 +0300 Subject: [PATCH 12/25] Added new option `--disable-coverage-php` (#6761) --- src/Codeception/Codecept.php | 55 +++++++-------- src/Codeception/Command/Run.php | 69 ++++++++++--------- .../Coverage/Subscriber/Printer.php | 6 +- tests/cli/IncludedCest.php | 13 +++- 4 files changed, 81 insertions(+), 62 deletions(-) diff --git a/src/Codeception/Codecept.php b/src/Codeception/Codecept.php index 0689f02c3c..46dd63ab09 100644 --- a/src/Codeception/Codecept.php +++ b/src/Codeception/Codecept.php @@ -45,33 +45,34 @@ class Codecept protected ExtensionLoader $extensionLoader; protected array $options = [ - 'silent' => false, - 'debug' => false, - 'steps' => false, - 'html' => false, - 'xml' => false, - 'phpunit-xml' => false, - 'no-redirect' => true, - 'report' => false, - 'colors' => false, - 'coverage' => false, - 'coverage-xml' => false, - 'coverage-html' => false, - 'coverage-text' => false, - 'coverage-crap4j' => false, - 'coverage-cobertura' => false, - 'coverage-phpunit' => false, - 'groups' => null, - 'excludeGroups' => null, - 'filter' => null, - 'shard' => null, - 'env' => null, - 'fail-fast' => 0, - 'ansi' => true, - 'verbosity' => 1, - 'interactive' => true, - 'no-rebuild' => false, - 'quiet' => false, + 'silent' => false, + 'debug' => false, + 'steps' => false, + 'html' => false, + 'xml' => false, + 'phpunit-xml' => false, + 'no-redirect' => true, + 'report' => false, + 'colors' => false, + 'coverage' => false, + 'coverage-xml' => false, + 'coverage-html' => false, + 'coverage-text' => false, + 'coverage-crap4j' => false, + 'coverage-cobertura' => false, + 'coverage-phpunit' => false, + 'disable-coverage-php' => false, + 'groups' => null, + 'excludeGroups' => null, + 'filter' => null, + 'shard' => null, + 'env' => null, + 'fail-fast' => 0, + 'ansi' => true, + 'verbosity' => 1, + 'interactive' => true, + 'no-rebuild' => false, + 'quiet' => false, ]; protected array $config = []; diff --git a/src/Codeception/Command/Run.php b/src/Codeception/Command/Run.php index 398c6cdeae..ac326b4b70 100644 --- a/src/Codeception/Command/Run.php +++ b/src/Codeception/Command/Run.php @@ -89,39 +89,40 @@ * * Options: * -o, --override=OVERRIDE Override config values (multiple values allowed) - * --config (-c) Use custom path for config - * --report Show output in compact style - * --html Generate html with results (default: "report.html") - * --xml Generate JUnit XML Log (default: "report.xml") - * --phpunit-xml Generate PhpUnit XML Log (default: "phpunit-report.xml") - * --no-redirect Do not redirect to Composer-installed version in vendor/codeception - * --colors Use colors in output - * --no-colors Force no colors in output (useful to override config file) - * --silent Only outputs suite names and final results. Almost the same as `--quiet` - * --steps Show steps in output - * --debug (-d) Alias for `-vv` - * --bootstrap Execute bootstrap script before the test - * --coverage Run with code coverage (default: "coverage.serialized") - * --coverage-html Generate CodeCoverage HTML report in path (default: "coverage") - * --coverage-xml Generate CodeCoverage XML report in file (default: "coverage.xml") - * --coverage-text Generate CodeCoverage text report in file (default: "coverage.txt") - * --coverage-phpunit Generate CodeCoverage PHPUnit report in file (default: "coverage-phpunit") - * --coverage-cobertura Generate CodeCoverage Cobertura report in file (default: "coverage-cobertura") - * --no-exit Don't finish with exit code - * --group (-g) Groups of tests to be executed (multiple values allowed) - * --skip (-s) Skip selected suites (multiple values allowed) - * --skip-group (-x) Skip selected groups (multiple values allowed) - * --env Run tests in selected environments. (multiple values allowed, environments can be merged with ',') - * --fail-fast (-f) Stop after nth failure (defaults to 1) - * --no-rebuild Do not rebuild actor classes on start - * --help (-h) Display this help message. - * --quiet (-q) Do not output any message. Almost the same as `--silent` - * --verbose (-v|vv|vvv) Increase the verbosity of messages: `v` for normal output, `vv` for steps and debug, `vvv` for Codeception-internal debug - * --version (-V) Display this application version. - * --ansi Force ANSI output. - * --no-ansi Disable ANSI output. - * --no-interaction (-n) Do not ask any interactive question. - * --seed Use the given seed for shuffling tests + * --config (-c) Use custom path for config + * --report Show output in compact style + * --html Generate html with results (default: "report.html") + * --xml Generate JUnit XML Log (default: "report.xml") + * --phpunit-xml Generate PhpUnit XML Log (default: "phpunit-report.xml") + * --no-redirect Do not redirect to Composer-installed version in vendor/codeception + * --colors Use colors in output + * --no-colors Force no colors in output (useful to override config file) + * --silent Only outputs suite names and final results. Almost the same as `--quiet` + * --steps Show steps in output + * --debug (-d) Alias for `-vv` + * --bootstrap Execute bootstrap script before the test + * --coverage Run with code coverage (default: "coverage.serialized") + * --disable-coverage-php Don't generate CodeCoverage report in raw PHP serialized format + * --coverage-html Generate CodeCoverage HTML report in path (default: "coverage") + * --coverage-xml Generate CodeCoverage XML report in file (default: "coverage.xml") + * --coverage-text Generate CodeCoverage text report in file (default: "coverage.txt") + * --coverage-phpunit Generate CodeCoverage PHPUnit report in file (default: "coverage-phpunit") + * --coverage-cobertura Generate CodeCoverage Cobertura report in file (default: "coverage-cobertura") + * --no-exit Don't finish with exit code + * --group (-g) Groups of tests to be executed (multiple values allowed) + * --skip (-s) Skip selected suites (multiple values allowed) + * --skip-group (-x) Skip selected groups (multiple values allowed) + * --env Run tests in selected environments. (multiple values allowed, environments can be merged with ',') + * --fail-fast (-f) Stop after nth failure (defaults to 1) + * --no-rebuild Do not rebuild actor classes on start + * --help (-h) Display this help message. + * --quiet (-q) Do not output any message. Almost the same as `--silent` + * --verbose (-v|vv|vvv) Increase the verbosity of messages: `v` for normal output, `vv` for steps and debug, `vvv` for Codeception-internal debug + * --version (-V) Display this application version. + * --ansi Force ANSI output. + * --no-ansi Disable ANSI output. + * --no-interaction (-n) Do not ask any interactive question. + * --seed Use the given seed for shuffling tests * ``` * */ @@ -173,6 +174,7 @@ protected function configure(): void ->addOption('coverage-crap4j', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage report in Crap4J XML format') ->addOption('coverage-cobertura', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage report in Cobertura XML format') ->addOption('coverage-phpunit', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage PHPUnit report in path') + ->addOption('disable-coverage-php', '', InputOption::VALUE_NONE, "Don't generate CodeCoverage report in raw PHP serialized format") ->addOption('no-exit', '', InputOption::VALUE_NONE, "Don't finish with exit code") ->addOption('group', 'g', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Groups of tests to be executed') ->addOption('skip', 's', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Skip selected suites') @@ -240,6 +242,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $userOptions['verbosity'] = $this->output->getVerbosity(); $userOptions['interactive'] = !$input->hasParameterOption(['--no-interaction', '-n']); $userOptions['ansi'] = (!$input->hasParameterOption('--no-ansi') xor $input->hasParameterOption('ansi')); + $userOptions['disable-coverage-php'] = (bool) $this->options['disable-coverage-php']; $userOptions['seed'] = $this->options['seed'] ? (int)$this->options['seed'] : rand(); if ($this->options['no-colors'] || !$userOptions['ansi']) { diff --git a/src/Codeception/Coverage/Subscriber/Printer.php b/src/Codeception/Coverage/Subscriber/Printer.php index b1ddabb2da..718d46476b 100644 --- a/src/Codeception/Coverage/Subscriber/Printer.php +++ b/src/Codeception/Coverage/Subscriber/Printer.php @@ -83,7 +83,11 @@ public function printResult(PrintResultEvent $event): void $this->printConsole(); } $this->output->write("Remote CodeCoverage reports are not printed to console\n"); - $this->printPHP(); + if ($this->options['disable-coverage-php'] === true) { + $this->output->write("PHP serialized report was skipped\n"); + } else { + $this->printPHP(); + } $this->output->write("\n"); $reports = [ diff --git a/tests/cli/IncludedCest.php b/tests/cli/IncludedCest.php index fb7fb343db..0feaf51559 100644 --- a/tests/cli/IncludedCest.php +++ b/tests/cli/IncludedCest.php @@ -125,10 +125,11 @@ public function runIncludedWithHtmlOutput(CliGuy $I) #[Before('moveToIncluded')] #[Group('coverage')] - public function runIncludedWithCoverage(CliGuy $I) + public function runIncludedWithCoverage(CliGuy $I): void { $I->executeCommand('run --coverage-xml'); $I->amInPath('_log'); + $I->seeFileFound('coverage.serialized'); $I->seeFileFound('coverage.xml'); //these assertions shrank over the years to be compatible with many versions of php-code-coverage library $I->seeInThisFile('BillEvans" namespace="'); @@ -136,6 +137,16 @@ public function runIncludedWithCoverage(CliGuy $I) $I->seeInThisFile('Hobbit" namespace="'); } + #[Before('moveToIncluded')] + #[Group('coverage')] + public function runIncludedWithoutPhpReport(CliGuy $I): void + { + $I->executeCommand('run --coverage-text --disable-coverage-php'); + $I->amInPath('_log'); + $I->seeFileFound('coverage.txt'); + $I->cantSeeFileFound('coverage.serialized'); + } + #[Before('moveToIncluded')] public function buildIncluded(CliGuy $I) { From e2c300b74c1e05618941b74f0e4a13ac7a05d257 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Fri, 13 Sep 2024 11:35:02 +0200 Subject: [PATCH 13/25] chore: add branch alias for main to fix composer install with dev deps (#6787) --- composer.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8e14508e4d..d462bd24c0 100644 --- a/composer.json +++ b/composer.json @@ -68,7 +68,11 @@ "replace": { "codeception/phpunit-wrapper": "*" }, - + "extra": { + "branch-alias": { + "dev-main": "5.2.x-dev" + } + }, "autoload": { "classmap": [ "src/PHPUnit/TestCase.php" @@ -112,6 +116,7 @@ "codeception/module-db": "source", "codeception/module-filesystem": "source", "codeception/module-phpbrowser": "source", + "codeception/lib-innerbrowser": "source", "*": "dist" } } From 37d49d72fe0d62728ca558b20e6ca495fa6ac789 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 13 Sep 2024 11:54:48 +0200 Subject: [PATCH 14/25] Update GenerateScenarios.php: Adding deprecation note (#6792) Repeating https://github.com/Codeception/Codeception/pull/6758 --- src/Codeception/Command/GenerateScenarios.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Codeception/Command/GenerateScenarios.php b/src/Codeception/Command/GenerateScenarios.php index 85ed70c0ed..452e392ffd 100644 --- a/src/Codeception/Command/GenerateScenarios.php +++ b/src/Codeception/Command/GenerateScenarios.php @@ -76,6 +76,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $tests = $this->getTests($suiteManager); $scenarios = ''; + $output->writeln('This command is deprecated and will be removed in the next major version of Codeception.'); + foreach ($tests as $test) { if (!$test instanceof ScenarioDriven || !$test instanceof Descriptive) { continue; From 3d7a135b76f29eba80917db17ef5653fb1c4f885 Mon Sep 17 00:00:00 2001 From: Sam Mousa Date: Fri, 13 Sep 2024 13:35:42 +0200 Subject: [PATCH 15/25] chore(ci): prevent test CI running twice on PR branches (#6788) --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc3d3b7a81..ba74f363cf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,8 @@ on: pull_request: branches-ignore: gh-pages push: - branches-ignore: gh-pages + branches: + - main name: build From fc707f5eec61e4d581968aa9c128f4550b0fd3c1 Mon Sep 17 00:00:00 2001 From: Aaron Gustavo Nieves <64917965+TavoNiievez@users.noreply.github.com> Date: Sun, 10 Nov 2024 13:29:44 -0500 Subject: [PATCH 16/25] Simplify Lib classes (#6767) --- src/Codeception/Lib/Actor/Shared/Pause.php | 2 - .../Shared/PhpSuperGlobalsConverter.php | 95 ++++------- src/Codeception/Lib/Console/Colorizer.php | 10 +- src/Codeception/Lib/Console/DiffFactory.php | 7 +- src/Codeception/Lib/Console/Message.php | 30 ++-- src/Codeception/Lib/Console/Output.php | 23 +-- src/Codeception/Lib/Console/ReplHistory.php | 4 +- src/Codeception/Lib/Di.php | 113 ++++++------- src/Codeception/Lib/Friend.php | 14 +- src/Codeception/Lib/Generator/Actions.php | 37 ++--- src/Codeception/Lib/Generator/Cest.php | 2 +- .../Lib/Generator/GherkinSnippets.php | 54 +++---- src/Codeception/Lib/Generator/Group.php | 2 - src/Codeception/Lib/Generator/Test.php | 9 +- src/Codeception/Lib/GroupManager.php | 71 ++++---- src/Codeception/Lib/ModuleContainer.php | 2 +- src/Codeception/Lib/ParamsLoader.php | 82 +++------- src/Codeception/Lib/Parser.php | 152 ++++++++---------- src/Codeception/Lib/PauseShell.php | 10 +- 19 files changed, 300 insertions(+), 419 deletions(-) diff --git a/src/Codeception/Lib/Actor/Shared/Pause.php b/src/Codeception/Lib/Actor/Shared/Pause.php index 0816d10202..a8d5574ef4 100644 --- a/src/Codeception/Lib/Actor/Shared/Pause.php +++ b/src/Codeception/Lib/Actor/Shared/Pause.php @@ -7,8 +7,6 @@ use Codeception\Command\Console; use Codeception\Lib\PauseShell; use Codeception\Util\Debug; -use Psy\Shell; -use Psy\Configuration; trait Pause { diff --git a/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php b/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php index 7932b9e5db..ca4296605e 100644 --- a/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php +++ b/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php @@ -8,101 +8,72 @@ * Converts BrowserKit\Request's request parameters and files into PHP-compatible structure * * @see https://bugs.php.net/bug.php?id=25589 - * @see https://bugs.php.net/bug.php?id=25589 + * @see https://bugs.php.net/bug.php?id=40000 * * @package Codeception\Lib\Connector */ trait PhpSuperGlobalsConverter { /** - * Rearrange files array to be compatible with PHP $_FILES superglobal structure - * @see https://bugs.php.net/bug.php?id=25589 + * Rearrange files array to match PHP $_FILES structure. + * Handles nested arrays within files, ensuring compatibility with PHP's $_FILES superglobal. */ protected function remapFiles(array $requestFiles): array { - $files = $this->rearrangeFiles($requestFiles); - - return $this->replaceSpaces($files); + $normalizedFiles = $this->normalizeFilesArray($requestFiles); + return $this->normalizeQueryParameters($normalizedFiles); } /** - * Escape high-level variable name with dots, underscores and other "special" chars - * to be compatible with PHP "bug" - * @see https://bugs.php.net/bug.php?id=40000 + * Normalize request parameters by replacing spaces and special characters. + * Ensures compatibility with PHP's handling of query parameters. */ protected function remapRequestParameters(array $parameters): array { - return $this->replaceSpaces($parameters); + return $this->normalizeQueryParameters($parameters); } - private function rearrangeFiles(array $requestFiles): array + private function normalizeFilesArray(array $requestFiles): array { - $files = []; - foreach ($requestFiles as $name => $info) { - if (!is_array($info)) { + $normalizedFiles = []; + foreach ($requestFiles as $fieldName => $fileInfo) { + if (!is_array($fileInfo)) { continue; } - /** - * If we have a form with fields like - * ``` - * - * - * ``` - * then only array variable will be used while simple variable will be ignored in php $_FILES - * (eg $_FILES = [ - * foo => [ - * tmp_name => [ - * 'bar' => 'asdf' - * ], - * //... - * ] - * ] - * ) - * (notice there is no entry for file "foo", only for file "foo[bar]" - * this will check if current element contains inner arrays within it's keys - * so we can ignore element itself and only process inner files - */ - $hasInnerArrays = count(array_filter($info, 'is_array')); + // Check if the current file info has nested arrays within its keys + $containsNestedArrays = count(array_filter($fileInfo, 'is_array')); - if ($hasInnerArrays || !isset($info['tmp_name'])) { - $inner = $this->remapFiles($info); - foreach ($inner as $innerName => $innerInfo) { - /** - * Convert from ['a' => ['tmp_name' => '/tmp/test.txt'] ] - * to ['tmp_name' => ['a' => '/tmp/test.txt'] ] - */ - $innerInfo = array_map( - fn ($v): array => [$innerName => $v], - $innerInfo + if ($containsNestedArrays || !isset($fileInfo['tmp_name'])) { + $nestedFiles = $this->remapFiles($fileInfo); + // Convert from ['a' => ['tmp_name' => '/tmp/test.txt'] ] + // to ['tmp_name' => ['a' => '/tmp/test.txt'] ] + foreach ($nestedFiles as $nestedFieldName => $nestedFileInfo) { + $nestedFileInfo = array_map( + fn($value): array => [$nestedFieldName => $value], + $nestedFileInfo ); - if (empty($files[$name])) { - $files[$name] = []; - } - - $files[$name] = array_replace_recursive($files[$name], $innerInfo); + $normalizedFiles[$fieldName] = array_replace_recursive( + $normalizedFiles[$fieldName] ?? [], + $nestedFileInfo + ); } } else { - $files[$name] = $info; + $normalizedFiles[$fieldName] = $fileInfo; } } - return $files; + return $normalizedFiles; } /** - * Replace spaces and dots and other chars in high-level query parameters for - * compatibility with PHP bug (or not a bug) - * @see https://bugs.php.net/bug.php?id=40000 - * - * @param array $parameters Array of request parameters to be converted + * Normalize query parameters by replacing spaces and special characters. + * Ensures compatibility with PHP's handling of query strings. */ - private function replaceSpaces(array $parameters): array + private function normalizeQueryParameters(array $parameters): array { - $qs = http_build_query($parameters); - parse_str($qs, $output); - - return $output; + parse_str(http_build_query($parameters), $normalizedParameters); + return $normalizedParameters; } } diff --git a/src/Codeception/Lib/Console/Colorizer.php b/src/Codeception/Lib/Console/Colorizer.php index 59a09134ec..1f69432d24 100644 --- a/src/Codeception/Lib/Console/Colorizer.php +++ b/src/Codeception/Lib/Console/Colorizer.php @@ -10,13 +10,11 @@ class Colorizer { public function colorize(string $string = ''): string { - $fp = fopen('php://memory', 'r+'); - fwrite($fp, $string); - rewind($fp); - + $lines = explode("\n", $string); $colorizedMessage = ''; - while ($line = fgets($fp)) { - $char = $line[0]; + + foreach ($lines as $line) { + $char = $line[0] ?? ''; $line = OutputFormatter::escape(trim($line)); switch ($char) { diff --git a/src/Codeception/Lib/Console/DiffFactory.php b/src/Codeception/Lib/Console/DiffFactory.php index 7160079890..25da4d4566 100644 --- a/src/Codeception/Lib/Console/DiffFactory.php +++ b/src/Codeception/Lib/Console/DiffFactory.php @@ -17,12 +17,7 @@ public function createDiff(ComparisonFailure $failure): string private function getDiff(string $expected = '', string $actual = ''): string { - if (!$actual && !$expected) { - return ''; - } - $differ = new Differ(new UnifiedDiffOutputBuilder('')); - - return $differ->diff($expected, $actual); + return ($expected || $actual) ? $differ->diff($expected, $actual) : ''; } } diff --git a/src/Codeception/Lib/Console/Message.php b/src/Codeception/Lib/Console/Message.php index e4ae493e65..0d54d93296 100644 --- a/src/Codeception/Lib/Console/Message.php +++ b/src/Codeception/Lib/Console/Message.php @@ -13,10 +13,9 @@ public function __construct(protected string $message, protected ?Output $output { } - public function with($param): self + public function with(...$params): self { - $args = array_merge([$this->message], func_get_args()); - $this->message = sprintf(...$args); + $this->message = sprintf($this->message, ...$params); return $this; } @@ -29,7 +28,6 @@ public function style(string $name): self public function width(int $length, string $char = ' '): self { $messageLength = $this->getLength(); - if ($messageLength < $length) { $this->message .= str_repeat($char, $length - $messageLength); } @@ -44,42 +42,33 @@ public function cut(int $length): self public function write(int $verbose = OutputInterface::VERBOSITY_NORMAL): void { - if ($verbose > $this->output->getVerbosity()) { - return; + if ($verbose <= $this->output->getVerbosity()) { + $this->output->write($this->message); } - $this->output->write($this->message); } public function writeln(int $verbose = OutputInterface::VERBOSITY_NORMAL): void { - if ($verbose > $this->output->getVerbosity()) { - return; + if ($verbose <= $this->output->getVerbosity()) { + $this->output->writeln($this->message); } - $this->output->writeln($this->message); } public function prepend(Message|string $string): self { - if ($string instanceof Message) { - $string = $string->getMessage(); - } - $this->message = $string . $this->message; + $this->message = ($string instanceof Message ? $string->getMessage() : $string) . $this->message; return $this; } public function append(Message|string $string): self { - if ($string instanceof Message) { - $string = $string->getMessage(); - } - $this->message .= $string; - + $this->message .= $string instanceof Message ? $string->getMessage() : $string; return $this; } public function apply(callable $func): self { - $this->message = call_user_func($func, $this->message); + $this->message = $func($this->message); return $this; } @@ -97,7 +86,6 @@ public function getMessage(): string public function block(string $style): self { $this->message = $this->output->formatHelper->formatBlock($this->message, $style, true); - return $this; } diff --git a/src/Codeception/Lib/Console/Output.php b/src/Codeception/Lib/Console/Output.php index 4e4160deef..dc8a259d1e 100644 --- a/src/Codeception/Lib/Console/Output.php +++ b/src/Codeception/Lib/Console/Output.php @@ -31,13 +31,20 @@ public function __construct(array $config) { $this->config = array_merge($this->config, $config); - // enable interactive output mode for CLI $this->isInteractive = $this->config['interactive'] && isset($_SERVER['TERM']) && PHP_SAPI === 'cli' && $_SERVER['TERM'] != 'linux'; $formatter = new OutputFormatter($this->config['colors']); + $this->configureStyles($formatter); + + $this->formatHelper = new SymfonyFormatterHelper(); + parent::__construct($this->config['verbosity'], $this->config['colors'], $formatter); + } + + protected function configureStyles(OutputFormatter $formatter): void + { $formatter->setStyle('default', new OutputFormatterStyle()); $formatter->setStyle('bold', new OutputFormatterStyle(null, null, ['bold'])); $formatter->setStyle('focus', new OutputFormatterStyle('magenta', null, ['bold'])); @@ -48,21 +55,16 @@ public function __construct(array $config) $formatter->setStyle('debug', new OutputFormatterStyle('cyan')); $formatter->setStyle('comment', new OutputFormatterStyle('yellow')); $formatter->setStyle('info', new OutputFormatterStyle('green')); - - $this->formatHelper = new SymfonyFormatterHelper(); - - parent::__construct($this->config['verbosity'], $this->config['colors'], $formatter); } - public function isInteractive(): bool + protected function clean(string $message): string { - return $this->isInteractive; + return str_replace('\/', '/', $message); } - protected function clean(string $message): string + public function isInteractive(): bool { - // clear json serialization - return str_replace('\/', '/', $message); + return $this->isInteractive; } public function debug(mixed $message): void @@ -91,7 +93,6 @@ public function message($message): Message public function exception(Exception $exception): void { $class = $exception::class; - $this->writeln(""); $this->writeln(sprintf('(![ %s ]!)', $class)); $this->writeln($exception->getMessage()); diff --git a/src/Codeception/Lib/Console/ReplHistory.php b/src/Codeception/Lib/Console/ReplHistory.php index a6ecc34d06..be1ce097e1 100644 --- a/src/Codeception/Lib/Console/ReplHistory.php +++ b/src/Codeception/Lib/Console/ReplHistory.php @@ -23,7 +23,7 @@ private function __construct() public static function getInstance(): ReplHistory { - if (static::$instance == null) { + if (static::$instance === null) { static::$instance = new self(); } @@ -52,9 +52,7 @@ public function save(): void } file_put_contents($this->outputFile, implode("\n", $this->stashedCommands) . "\n", FILE_APPEND); - codecept_debug("Stashed commands have been saved to {$this->outputFile}"); - $this->clear(); } } diff --git a/src/Codeception/Lib/Di.php b/src/Codeception/Lib/Di.php index ad1e6c09ef..a98277b3b4 100644 --- a/src/Codeception/Lib/Di.php +++ b/src/Codeception/Lib/Di.php @@ -34,7 +34,6 @@ public function __construct(?Di $fallback = null) public function get(string $className): ?object { - // normalize namespace $className = ltrim($className, '\\'); return $this->container[$className] ?? null; } @@ -54,49 +53,42 @@ public function instantiate( ?array $constructorArgs = null, string $injectMethodName = self::DEFAULT_INJECT_METHOD_NAME ): ?object { - // normalize namespace $className = ltrim($className, '\\'); - // get class from container if (isset($this->container[$className])) { if ($this->container[$className] instanceof $className) { return $this->container[$className]; } - throw new InjectionException("Failed to resolve cyclic dependencies for class '{$className}'"); } - // get class from parent container if ($this->fallback instanceof Di && ($class = $this->fallback->get($className))) { return $class; } - $this->container[$className] = false; // flag that object is being instantiated + $this->container[$className] = false; + + try { + $reflectedClass = new ReflectionClass($className); + } catch (ReflectionException $e) { + throw new InjectionException("Failed to create instance of '{$className}'. " . $e->getMessage()); + } - $reflectedClass = new ReflectionClass($className); if (!$reflectedClass->isInstantiable()) { return null; } - $reflectedConstructor = $reflectedClass->getConstructor(); - if (is_null($reflectedConstructor)) { - $object = new $className(); - } else { - try { - if (!$constructorArgs) { - $constructorArgs = $this->prepareArgs($reflectedConstructor); - } - } catch (Exception $e) { - throw new InjectionException("Failed to create instance of '{$className}'. " . $e->getMessage()); - } - $object = $reflectedClass->newInstanceArgs($constructorArgs); - } + $constructorArgs = $constructorArgs ?? $this->prepareArgs($reflectedClass->getConstructor()); - if ($injectMethodName !== '') { - $this->injectDependencies($object, $injectMethodName); + try { + $object = $reflectedClass->newInstanceArgs($constructorArgs ?? []); + } catch (ReflectionException $e) { + throw new InjectionException("Failed to create instance of '{$className}'. " . $e->getMessage()); } + $this->injectDependencies($object, $injectMethodName); $this->container[$className] = $object; + return $object; } @@ -107,59 +99,60 @@ public function instantiate( public function injectDependencies(object $object, string $injectMethodName = self::DEFAULT_INJECT_METHOD_NAME, array $defaults = []): void { $reflectedObject = new ReflectionObject($object); - $reflectionObjectHasMethod = $reflectedObject->hasMethod($injectMethodName); - if (!$reflectionObjectHasMethod) { - return; - } - $reflectedMethod = $reflectedObject->getMethod($injectMethodName); - try { - $args = $this->prepareArgs($reflectedMethod, $defaults); - } catch (Exception $e) { - $msg = $e->getMessage(); - if ($e->getPrevious() instanceof Throwable) { // injection failed because PHP code is invalid. See #3869 - $msg .= '; ' . $e->getPrevious(); + if ($reflectedObject->hasMethod($injectMethodName)) { + $reflectedMethod = $reflectedObject->getMethod($injectMethodName); + + try { + $args = $this->prepareArgs($reflectedMethod, $defaults); + } catch (Exception $e) { + $msg = $e->getMessage(); + if ($e->getPrevious() instanceof Throwable) { + $msg .= '; ' . $e->getPrevious(); + } + throw new InjectionException( + "Failed to inject dependencies in instance of '{$reflectedObject->name}'. {$msg}" + ); } - throw new InjectionException( - "Failed to inject dependencies in instance of '{$reflectedObject->name}'. {$msg}" - ); - } - if (!$reflectedMethod->isPublic()) { $reflectedMethod->setAccessible(true); + $reflectedMethod->invokeArgs($object, $args); } - $reflectedMethod->invokeArgs($object, $args); } - protected function prepareArgs(ReflectionMethod $method, array $defaults = []): array + protected function prepareArgs(ReflectionMethod $method = null, array $defaults = []): array { $args = []; - $parameters = $method->getParameters(); - foreach ($parameters as $k => $parameter) { - $dependency = ReflectionHelper::getClassFromParameter($parameter); - if (is_null($dependency)) { - if ($parameter->isVariadic()) { - continue; - } - if (!$parameter->isOptional()) { - if (!isset($defaults[$k])) { - throw new InjectionException("Parameter '{$parameter->name}' must have default value."); - } - $args[] = $defaults[$k]; - continue; - } - $args[] = $parameter->getDefaultValue(); - } else { - $arg = $this->instantiate($dependency); - if (is_null($arg)) { + + if ($method !== null) { + foreach ($method->getParameters() as $k => $parameter) { + $dependency = ReflectionHelper::getClassFromParameter($parameter); + + if (is_null($dependency)) { if ($parameter->isVariadic()) { continue; } - throw new InjectionException("Failed to resolve dependency '{$dependency}'."); + + if (!$parameter->isOptional()) { + $args[] = $defaults[$k] ?? throw new InjectionException("Parameter '{$parameter->name}' must have default value."); + } else { + $args[] = $parameter->getDefaultValue(); + } + } else { + try { + $arg = $this->instantiate($dependency); + } catch (ReflectionException $e) { + throw new InjectionException("Failed to resolve dependency '{$dependency}'. " . $e->getMessage()); + } + + if (is_null($arg) && !$parameter->isVariadic()) { + throw new InjectionException("Failed to resolve dependency '{$dependency}'."); + } + $args[] = $arg; } - $args[] = $arg; } } + return $args; } } diff --git a/src/Codeception/Lib/Friend.php b/src/Codeception/Lib/Friend.php index 7c583c9c38..b73200398b 100644 --- a/src/Codeception/Lib/Friend.php +++ b/src/Codeception/Lib/Friend.php @@ -16,14 +16,14 @@ class Friend public function __construct(protected string $name, protected Actor $actor, array $modules = []) { - $this->multiSessionModules = array_filter($modules, fn ($m): bool => $m instanceof MultiSession); + $this->multiSessionModules = array_filter($modules, fn($m): bool => $m instanceof MultiSession); if ($this->multiSessionModules === []) { throw new TestRuntimeException("No multisession modules used. Can't instantiate friend"); } } - public function does($closure) + public function does(callable $closure) { $currentUserData = []; @@ -39,7 +39,7 @@ public function does($closure) } $this->actor->comment(strtoupper("{$this->name} does ---")); - $ret = $closure($this->actor); + $result = $closure($this->actor); $this->actor->comment(strtoupper("--- {$this->name} finished")); foreach ($this->multiSessionModules as $module) { @@ -47,7 +47,8 @@ public function does($closure) $this->data[$name] = $module->_backupSession(); $module->_loadSession($currentUserData[$name]); } - return $ret; + + return $result; } public function isGoingTo(string $argumentation): void @@ -68,8 +69,9 @@ public function expectsTo(string $prediction): void public function leave(): void { foreach ($this->multiSessionModules as $module) { - if (isset($this->data[$module->_getName()])) { - $module->_closeSession($this->data[$module->_getName()]); + $name = $module->_getName(); + if (isset($this->data[$name])) { + $module->_closeSession($this->data[$name]); } } } diff --git a/src/Codeception/Lib/Generator/Actions.php b/src/Codeception/Lib/Generator/Actions.php index 4de4d09c6b..7e164dc2e3 100644 --- a/src/Codeception/Lib/Generator/Actions.php +++ b/src/Codeception/Lib/Generator/Actions.php @@ -87,15 +87,18 @@ public function __construct(array $settings) $this->name = $settings['actor']; $this->settings = $settings; $this->di = new Di(); - $modules = Configuration::modules($this->settings); $this->moduleContainer = new ModuleContainer($this->di, $settings); - foreach ($modules as $moduleName) { + $this->initializeModules(); + } + + protected function initializeModules() + { + foreach (Configuration::modules($this->settings) as $moduleName) { $this->moduleContainer->create($moduleName); } $this->modules = $this->moduleContainer->all(); $this->actions = $this->moduleContainer->getActions(); - - $this->generatedSteps = (array)$settings['step_decorators']; + $this->generatedSteps = (array)$this->settings['step_decorators']; } public function produce(): string @@ -105,14 +108,13 @@ public function produce(): string $methods = []; $code = []; foreach ($this->actions as $action => $moduleName) { - if (in_array($action, $methods)) { - continue; + if (!in_array($action, $methods)) { + $class = new ReflectionClass($this->modules[$moduleName]); + $method = $class->getMethod($action); + $code[] = $this->addMethod($method); + $methods[] = $action; + ++$this->numMethods; } - $class = new ReflectionClass($this->modules[$moduleName]); - $method = $class->getMethod($action); - $code[] = $this->addMethod($method); - $methods[] = $action; - ++$this->numMethods; } return (new Template($this->template)) @@ -251,11 +253,6 @@ public static function genHash(array $modules, array $settings): string return md5(Codecept::VERSION . serialize($actions) . serialize($settings['modules']) . implode(',', (array)$settings['step_decorators'])); } - public function getNumMethods(): int - { - return $this->numMethods; - } - private function createReturnTypeHint(ReflectionMethod $refMethod): string { $returnType = $refMethod->getReturnType(); @@ -322,10 +319,14 @@ private function stringifyAttribute(ReflectionAttribute $attribute): string // If we can't get the class then just return what we've been given. $name = $attribute->getName(); } - $arguments = $attribute->getArguments(); // Strip the wrapping array brackets so parameters aren't converted to arrays. - $args = substr(ReflectionHelper::phpEncodeValue($arguments), 1, -1); + $args = substr(ReflectionHelper::phpEncodeValue($attribute->getArguments()), 1, -1); return '#[' . $name . '(' . $args . ')]'; } + + public function getNumMethods(): int + { + return $this->numMethods; + } } diff --git a/src/Codeception/Lib/Generator/Cest.php b/src/Codeception/Lib/Generator/Cest.php index 8c09ddcf32..f3f52008f6 100644 --- a/src/Codeception/Lib/Generator/Cest.php +++ b/src/Codeception/Lib/Generator/Cest.php @@ -50,7 +50,7 @@ public function produce(): string throw new ConfigurationException("Cest can't be created for suite without an actor. Add `actor: SomeTester` to suite config"); } - $namespaceHeader = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst((string) $this->settings['suite']) . '\\' . $this->name); + $namespaceHeader = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst((string)$this->settings['suite']) . '\\' . $this->name); if ($namespaceHeader) { $namespaceHeader .= "\nuse " . $this->supportNamespace() . $actor . ";"; diff --git a/src/Codeception/Lib/Generator/GherkinSnippets.php b/src/Codeception/Lib/Generator/GherkinSnippets.php index 0c8e0cc8f9..ee31dbcc0e 100644 --- a/src/Codeception/Lib/Generator/GherkinSnippets.php +++ b/src/Codeception/Lib/Generator/GherkinSnippets.php @@ -40,10 +40,10 @@ public function {{methodName}}({{params}}) public function __construct(array $settings, $test = null) { $loader = new Gherkin($settings); - $pattern = $loader->getPattern(); $path = $settings['path']; - if (!empty($test)) { - $path = $settings['path'] . '/' . $test; + $pattern = $loader->getPattern(); + if ($test) { + $path = "$path/$test"; if (preg_match($pattern, $test)) { $path = dirname($path); $pattern = basename($test); @@ -58,8 +58,7 @@ public function __construct(array $settings, $test = null) ->name($pattern); foreach ($finder as $file) { - $pathname = str_replace("//", "/", $file->getPathname()); - $loader->loadTests($pathname); + $loader->loadTests($file->getPathname()); } $availableSteps = $loader->getSteps(); $allSteps = []; @@ -101,41 +100,34 @@ public function addSnippet(StepNode $step): void $pattern = $step->getText(); // match numbers (not in quotes) - if (preg_match_all('#([\d.])(?=([^"]*"[^"]*")*[^"]*$)#', $pattern, $matches)) { - foreach ($matches[1] as $num => $param) { - ++$num; - $args[] = '$num' . $num; - $pattern = str_replace($param, ":num{$num}", $pattern); - } - } - - // match quoted string - if (preg_match_all('#"(.*?)"#', $pattern, $matches)) { - foreach ($matches[1] as $num => $param) { - ++$num; - $args[] = '$arg' . $num; - $pattern = str_replace('"' . $param . '"', ":arg{$num}", $pattern); - } - } - // Has multiline argument at the end of step? + $pattern = preg_replace_callback('#([\d.])(?=([^"]*"[^"]*")*[^"]*$)#', function () use (&$args) { + $args[] = '$num' . (count($args) + 1); + return ":num" . count($args); + }, $pattern); + + // match quoted strings + $pattern = preg_replace_callback('#"(.*?)"#', function () use (&$args) { + $args[] = '$arg' . (count($args) + 1); + return ":arg" . count($args); + }, $pattern); + + // add multiline argument if present if (self::stepHasPyStringArgument($step)) { - $num = count($args) + 1; - $pattern .= " :arg{$num}"; - $args[] = '$arg' . $num; + $args[] = '$arg' . (count($args) + 1); + $pattern .= " :arg" . count($args); } + if (in_array($pattern, $this->processed)) { return; } $methodName = preg_replace('#(\s+?|\'|\"|\W)#', '', ucwords(preg_replace('#"(.*?)"|\d+#', '', $step->getText()))); - if (empty($methodName)) { - $methodName = 'step_' . substr(sha1($pattern), 0, 9); - } + $methodName = empty($methodName) ? 'step_' . substr(sha1($pattern), 0, 9) : lcfirst($methodName); $this->snippets[] = (new Template($this->template)) ->place('type', $step->getKeywordType()) ->place('text', $pattern) - ->place('methodName', lcfirst($methodName)) + ->place('methodName', $methodName) ->place('params', implode(', ', $args)) ->produce(); @@ -162,9 +154,7 @@ public static function stepHasPyStringArgument(StepNode $step): bool { if ($step->hasArguments()) { $stepArgs = $step->getArguments(); - if ($stepArgs[count($stepArgs) - 1]->getNodeType() == "PyString") { - return true; - } + return end($stepArgs)->getNodeType() === 'PyString'; } return false; } diff --git a/src/Codeception/Lib/Generator/Group.php b/src/Codeception/Lib/Generator/Group.php index 0ea6056825..b23dcfa915 100644 --- a/src/Codeception/Lib/Generator/Group.php +++ b/src/Codeception/Lib/Generator/Group.php @@ -50,8 +50,6 @@ public function _after(TestEvent \$e) public function __construct(protected array $settings, protected string $name) { - $this->settings = $settings; - $this->name = $name; $this->namespace = $this->getNamespaceString($this->supportNamespace() . '\\Group\\' . $name); } diff --git a/src/Codeception/Lib/Generator/Test.php b/src/Codeception/Lib/Generator/Test.php index 74ee0ebf88..88cd2ff8e1 100644 --- a/src/Codeception/Lib/Generator/Test.php +++ b/src/Codeception/Lib/Generator/Test.php @@ -51,19 +51,18 @@ public function __construct(protected array $settings, string $name) public function produce(): string { $actor = $this->settings['actor']; - - $ns = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst((string) $this->settings['suite']) . '\\' . $this->name); + $namespacePath = $this->settings['namespace'] . '\\' . ucfirst((string)$this->settings['suite']) . '\\' . $this->name; + $ns = $this->getNamespaceHeader($namespacePath); if ($ns) { $ns .= "\nuse " . $this->supportNamespace() . $actor . ";"; } - $tester = ''; - if ($this->settings['actor']) { + if ($actor) { $tester = (new Template($this->testerTemplate)) ->place('actorClass', $actor) - ->place('actor', lcfirst((string) Configuration::config()['actor_suffix'])) + ->place('actor', lcfirst((string)Configuration::config()['actor_suffix'])) ->produce(); } diff --git a/src/Codeception/Lib/GroupManager.php b/src/Codeception/Lib/GroupManager.php index a193b3a897..3f29920a63 100644 --- a/src/Codeception/Lib/GroupManager.php +++ b/src/Codeception/Lib/GroupManager.php @@ -47,10 +47,8 @@ protected function loadGroupsByPattern(): void if (!str_contains($group, '*')) { continue; } - $path = dirname($pattern); - if (!PathResolver::isPathAbsolute($pattern)) { - $path = $this->rootDir . $path; - } + + $path = PathResolver::isPathAbsolute($pattern) ? dirname($pattern) : $this->rootDir . dirname($pattern); $files = Finder::create()->files() ->name(basename($pattern)) @@ -61,7 +59,6 @@ protected function loadGroupsByPattern(): void $prefix = str_replace('*', '', $group); $pathPrefix = str_replace('*', '', basename($pattern)); $groupName = $prefix . str_replace($pathPrefix, '', $file->getRelativePathname()); - $this->configuredGroups[$groupName] = dirname($pattern) . DIRECTORY_SEPARATOR . $file->getRelativePathname(); } @@ -73,49 +70,50 @@ protected function loadConfiguredGroupSettings(): void { foreach ($this->configuredGroups as $group => $tests) { $this->testsInGroups[$group] = []; - if (is_array($tests)) { - foreach ($tests as $test) { - $file = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], (string) $test); - $this->testsInGroups[$group][] = $this->normalizeFilePath($file, $group); - } - continue; - } + $testsArray = is_array($tests) ? $tests : $this->getTestsFromFile($tests); - $path = $tests; - if (!codecept_is_path_absolute($tests)) { - $path = $this->rootDir . $tests; + foreach ($testsArray as $test) { + $file = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $test); + $this->testsInGroups[$group][] = $this->normalizeFilePath($file, $group); } + } + } - if (is_file($path)) { - $handle = @fopen($path, "r"); - if ($handle) { - while (($test = fgets($handle, 4096)) !== false) { - // if the current line is blank then we need to move to the next line - // otherwise the current codeception directory becomes part of the group - // which causes every single test to run - if (trim($test) === '') { - continue; - } - - $file = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], trim($test)); - $this->testsInGroups[$group][] = $this->normalizeFilePath($file, $group); - } - fclose($handle); + private function getTestsFromFile(string $tests): array + { + $path = codecept_is_path_absolute($tests) ? $tests : $this->rootDir . $tests; + if (!is_file($path)) { + return []; + } + + $testsArray = []; + $handle = fopen($path, 'r'); + if ($handle) { + while (($test = fgets($handle, 4096)) !== false) { + // if the current line is blank then we need to move to the next line + // otherwise the current codeception directory becomes part of the group + // which causes every single test to run + if (trim($test) !== '') { + $testsArray[] = trim($test); } } + fclose($handle); } + return $testsArray; } private function normalizeFilePath(string $file, string $group): string { $pathParts = explode(':', $file); - if (codecept_is_path_absolute($file)) { + $isAbsolute = codecept_is_path_absolute($file); + + if ($isAbsolute) { if ($file[0] === '/' && count($pathParts) > 1) { - //take segment before first : + // Take segment before first : $this->checkIfFileExists($pathParts[0], $group); return sprintf('%s:%s', realpath($pathParts[0]), $pathParts[1]); } elseif (count($pathParts) > 2) { - //on Windows take segment before second : + // On Windows take segment before second : $fullPath = $pathParts[0] . ':' . $pathParts[1]; $this->checkIfFileExists($fullPath, $group); return sprintf('%s:%s', realpath($fullPath), $pathParts[2]); @@ -150,11 +148,7 @@ public function groupsForTest(Test $test): array foreach ($this->testsInGroups as $group => $tests) { /** @var string[] $tests */ foreach ($tests as $testPattern) { - if ($filename == $testPattern) { - $groups[] = $group; - } - - if (str_starts_with($filename . ':' . $testName, $testPattern)) { + if ($filename == $testPattern || str_starts_with($filename . ':' . $testName, $testPattern)) { $groups[] = $group; } if ( @@ -165,6 +159,7 @@ public function groupsForTest(Test $test): array } } } + return array_unique($groups); } } diff --git a/src/Codeception/Lib/ModuleContainer.php b/src/Codeception/Lib/ModuleContainer.php index 7868f930fd..1fa713640f 100644 --- a/src/Codeception/Lib/ModuleContainer.php +++ b/src/Codeception/Lib/ModuleContainer.php @@ -333,7 +333,7 @@ private function injectModuleDependencies(string $moduleName, DependsOnModule $m throw new ModuleException($module, 'Module requires method _inject to be defined to accept dependencies'); } - $dependencies = array_map(fn ($dependency): ?object => $this->create($dependency, false), $this->getConfiguredDependencies($moduleName)); + $dependencies = array_map(fn($dependency): ?object => $this->create($dependency, false), $this->getConfiguredDependencies($moduleName)); call_user_func_array([$module, '_inject'], $dependencies); } diff --git a/src/Codeception/Lib/ParamsLoader.php b/src/Codeception/Lib/ParamsLoader.php index b417787e37..b238186ffd 100644 --- a/src/Codeception/Lib/ParamsLoader.php +++ b/src/Codeception/Lib/ParamsLoader.php @@ -12,21 +12,17 @@ use Symfony\Component\Dotenv\Dotenv as SymfonyDotenv; use Symfony\Component\Yaml\Yaml; -use function class_exists; use function codecept_absolute_path; use function codecept_relative_path; -use function extension_loaded; use function file_exists; use function file_get_contents; -use function method_exists; use function parse_ini_file; use function preg_match; +use function simplexml_load_file; class ParamsLoader { /** - * @param array|string $paramStorage - * @return array * @throws ConfigurationException */ public static function load(array|string $paramStorage): array @@ -35,8 +31,8 @@ public static function load(array|string $paramStorage): array return $paramStorage; } - if ($paramStorage === 'env' || $paramStorage === 'environment') { - return self::loadEnvironmentVars(); + if (in_array($paramStorage, ['env', 'environment'])) { + return $_SERVER; } $paramsFile = codecept_absolute_path($paramStorage); @@ -44,35 +40,28 @@ public static function load(array|string $paramStorage): array throw new ConfigurationException("Params file {$paramsFile} not found"); } - try { - if (preg_match('#\.ya?ml$#', $paramStorage)) { - return self::loadYamlFile($paramsFile); + $loaderMappings = [ + 'loadYamlFile' => '#\.ya?ml$#', + 'loadIniFile' => '#\.ini$#', + 'loadPhpFile' => '#\.php$#', + 'loadDotEnvFile' => '#(\.env(\.|$))#', + 'loadXmlFile' => '#\.xml$#', + ]; + + foreach ($loaderMappings as $method => $pattern) { + if (preg_match($pattern, $paramStorage)) { + try { + return self::$method($paramStorage); + } catch (Exception $e) { + throw new ConfigurationException("Failed loading params from {$paramStorage}\n" . $e->getMessage()); + } } - - if (preg_match('#\.ini$#', $paramStorage)) { - return self::loadIniFile($paramsFile); - } - - if (preg_match('#\.php$#', $paramStorage)) { - return self::loadPhpFile($paramsFile); - } - - if (preg_match('#(\.env(\.|$))#', $paramStorage)) { - return self::loadDotEnvFile($paramsFile); - } - - if (preg_match('#\.xml$#', $paramStorage)) { - return self::loadXmlFile($paramsFile); - } - } catch (Exception $e) { - throw new ConfigurationException("Failed loading params from {$paramStorage}\n" . $e->getMessage()); } throw new ConfigurationException("Params can't be loaded from `{$paramStorage}`."); } /** - * @return array * @throws ConfigurationException */ private static function loadIniFile(string $file): array @@ -82,7 +71,6 @@ private static function loadIniFile(string $file): array } /** - * @return array * @throws ConfigurationException */ private static function loadPhpFile(string $file): array @@ -92,23 +80,15 @@ private static function loadPhpFile(string $file): array } /** - * @return array * @throws ConfigurationException */ private static function loadYamlFile(string $file): array { $params = Yaml::parse(self::getFileContents($file)); - $params = self::validateParams($params, $file); - - if (isset($params['parameters'])) { // Symfony style - $params = self::validateParams($params['parameters'], $file); - ; - } - return self::validateParams($params, $file); + return self::validateParams($params['parameters'] ?? $params, $file); } /** - * @return array * @throws ConfigurationException */ private static function loadXmlFile(string $file): array @@ -127,10 +107,9 @@ private static function loadXmlFile(string $file): array 'bool', 'boolean', 'int', 'integer', 'float', 'double' => settype($value, $type), 'constant' => constant($value), 'collection' => $paramsToArray($param), - default => (string) $param, + default => (string)$param, }; } - return $a; }; @@ -138,29 +117,25 @@ private static function loadXmlFile(string $file): array if ($simpleXMLElement === false) { throw new ConfigurationException("Params can't be loaded from `{$file}`."); } - $params = $paramsToArray($simpleXMLElement); + $params = $paramsToArray($simpleXMLElement); return self::validateParams($params, $file); } /** - * @return array * @throws ConfigurationException */ private static function loadDotEnvFile(string $file): array { - // vlucas/phpdotenv if ( - class_exists(PhpDotenv::class) - && class_exists(RepositoryBuilder::class) - && method_exists(RepositoryBuilder::class, 'createWithDefaultAdapters') + class_exists(PhpDotenv::class) && + class_exists(RepositoryBuilder::class) && + method_exists(RepositoryBuilder::class, 'createWithDefaultAdapters') ) { $repository = RepositoryBuilder::createWithDefaultAdapters()->make(); $dotenv = PhpDotenv::create($repository, codecept_root_dir(), codecept_relative_path($file)); - return $dotenv->load(); } - // symfony/dotenv if (class_exists(SymfonyDotenv::class)) { $symfonyDotEnv = new SymfonyDotenv(); $values = $symfonyDotEnv->parse(self::getFileContents($file), $file); @@ -174,14 +149,6 @@ class_exists(PhpDotenv::class) ); } - /** - * @return array - */ - private static function loadEnvironmentVars(): array - { - return $_SERVER; - } - /** * @throws ConfigurationException */ @@ -195,7 +162,6 @@ private static function getFileContents(string $file): string } /** - * @return array * @throws ConfigurationException */ private static function validateParams(mixed $params, string $file): array diff --git a/src/Codeception/Lib/Parser.php b/src/Codeception/Lib/Parser.php index 4c91a59d7f..6e47f99369 100644 --- a/src/Codeception/Lib/Parser.php +++ b/src/Codeception/Lib/Parser.php @@ -28,16 +28,10 @@ public function prepareToRun(string $code): void public function parseFeature(string $code): void { - $matches = []; $code = $this->stripComments($code); - $res = preg_match("#\\\$I->wantTo\\(\\s*?['\"](.*?)['\"]\\s*?\\);#", $code, $matches); - if ($res) { - $this->scenario->setFeature($matches[1]); - return; - } - $res = preg_match("#\\\$I->wantToTest\\(['\"](.*?)['\"]\\);#", $code, $matches); - if ($res) { - $this->scenario->setFeature("test " . $matches[1]); + if (preg_match("#\\\$I->(wantTo|wantToTest)\\(\\s*?['\"](.*?)['\"]\\s*?\\);#", $code, $matches)) { + $feature = $matches[1] === 'wantToTest' ? "test {$matches[2]}" : $matches[2]; + $this->scenario->setFeature($feature); } } @@ -48,17 +42,15 @@ public function parseScenarioOptions(string $code): void public function parseSteps(string $code): void { - // parse per line $friends = []; $lines = explode("\n", $code); $isFriend = false; + foreach ($lines as $line) { - // friends - if (preg_match("#\\\$I->haveFriend\\((.*?)\\);#", $line, $matches)) { + if (preg_match("#\\\$I->haveFriend\\((.*?)\\);#", $line, $matches)) { // Friends $friends[] = trim($matches[1], '\'"'); } - // friend's section start - if (preg_match("#\\\$(.*?)->does\\(#", $line, $matches)) { + if (preg_match("#\\\$(.*?)->does\\(#", $line, $matches)) { // Friends section start $friend = $matches[1]; if (!in_array($friend, $friends)) { continue; @@ -67,14 +59,10 @@ public function parseSteps(string $code): void $this->addCommentStep("\n----- {$friend} does -----"); continue; } - - // actions - if (preg_match("#\\\$I->(.*)\\((.*?)\\);#", $line, $matches)) { + if (preg_match("#\\\$I->(.*)\\((.*?)\\);#", $line, $matches)) { // Actions $this->addStep($matches); } - - // friend's section ends - if ($isFriend && str_contains($line, '}')) { + if ($isFriend && str_contains($line, '}')) { // Friends section ends $this->addCommentStep("-------- back to me\n"); $isFriend = false; } @@ -84,10 +72,9 @@ public function parseSteps(string $code): void protected function addStep(array $matches): void { [$m, $action, $params] = $matches; - if (in_array($action, ['wantTo', 'wantToTest'])) { - return; + if (!in_array($action, ['wantTo', 'wantToTest'])) { + $this->scenario->addStep(new Action($action, explode(',', $params))); } - $this->scenario->addStep(new Action($action, explode(',', $params))); } protected function addCommentStep(string $comment): void @@ -111,62 +98,69 @@ public static function load(string $file): void */ public static function getClassesFromFile(string $file): array { - $sourceCode = file_get_contents($file); - $classes = []; - $tokens = token_get_all($sourceCode, TOKEN_PARSE); - - $tokenCount = count($tokens); + $sourceCodeTokens = token_get_all(file_get_contents($file), TOKEN_PARSE); + $classes = []; $namespace = ''; - for ($i = 0; $i < $tokenCount; ++$i) { - if ($tokens[$i][0] === T_NAMESPACE) { - $namespace = ''; - for ($j = $i + 1; $j < $tokenCount; ++$j) { - if ($tokens[$j] === '{' || $tokens[$j] === ';') { - break; - } - if ($tokens[$j][0] === T_STRING || $tokens[$j][0] === T_NAME_QUALIFIED) { - $namespace .= $tokens[$j][1] . '\\'; - } - } + foreach ($sourceCodeTokens as $i => $token) { + if ($token[0] === T_NAMESPACE) { + $namespace = self::extractNamespace($sourceCodeTokens, $i); } - - if ($tokens[$i][0] === T_CLASS) { - // class at the beginning of file - if (!isset($tokens[$i - 2])) { - $classes[] = $namespace . $tokens[$i + 2][1]; - continue; - } - // new class - if ($tokens[$i - 2][0] === T_NEW) { - continue; + if ($token[0] === T_CLASS) { + $class = self::extractClass($sourceCodeTokens, $i); + if ($class) { + $classes[] = $namespace . $class; } - // :: class - if ($tokens[$i - 1][0] === T_WHITESPACE && $tokens[$i - 2][0] === T_DOUBLE_COLON) { - continue; - } - // ::class - if ($tokens[$i - 1][0] === T_DOUBLE_COLON) { - continue; - } - // class{ - if (isset($tokens[$i + 1]) && ($tokens[$i + 1] === '{')) { - continue; - } - // class { - if (isset($tokens[$i + 2]) && $tokens[$i + 1][0] === T_WHITESPACE && $tokens[$i + 2] === '{') { - continue; - } - $classes[] = $namespace . $tokens[$i + 2][1]; } } - $tokens = null; gc_mem_caches(); - return $classes; } + private static function extractNamespace(array $tokens, int $index): string + { + $namespace = ''; + for ($j = $index + 1; $j < count($tokens); ++$j) { + if ($tokens[$j] === '{' || $tokens[$j] === ';') { + break; + } + if ($tokens[$j][0] === T_STRING || $tokens[$j][0] === T_NAME_QUALIFIED) { + $namespace .= $tokens[$j][1] . '\\'; + } + } + return $namespace; + } + + private static function extractClass(array $tokens, int $index): ?string + { + // class at the beginning of file + if (!isset($tokens[$index - 2])) { + return $tokens[$index + 2][1] ?? null; + } + // new class + if (isset($tokens[$index - 2]) && $tokens[$index - 2][0] === T_NEW) { + return null; + } + // :: class + if (isset($tokens[$index - 1]) && $tokens[$index - 1][0] === T_WHITESPACE && isset($tokens[$index - 2]) && $tokens[$index - 2][0] === T_DOUBLE_COLON) { + return null; + } + // ::class + if (isset($tokens[$index - 1]) && $tokens[$index - 1][0] === T_DOUBLE_COLON) { + return null; + } + // class{ + if (isset($tokens[$index + 1]) && $tokens[$index + 1] === '{') { + return null; + } + // class { + if (isset($tokens[$index + 2]) && $tokens[$index + 1][0] === T_WHITESPACE && $tokens[$index + 2] === '{') { + return null; + } + return $tokens[$index + 2][1] ?? null; + } + /* * Include in different scope to prevent included file from affecting $file variable */ @@ -177,24 +171,16 @@ private static function includeFile(string $file): void protected function stripComments(string $code): string { - $code = preg_replace('#//.*?$#m', '', $code); // remove inline comments - return preg_replace('#/*\*.*?\*/#ms', '', $code); // remove block comment + return preg_replace(['#//.*?$#m', '#/*\*.*?\*/#ms'], '', $code); // inline & block comments } protected function matchComments(string $code): string { - $matches = []; - $comments = ''; - $hasLineComment = preg_match_all('#//(.*?)$#m', $code, $matches); - if ($hasLineComment) { - foreach ($matches[1] as $line) { - $comments .= $line . "\n"; - } - } - $hasBlockComment = preg_match('#/*\*(.*?)\*/#ms', $code, $matches); - if ($hasBlockComment) { - $comments .= $matches[1] . "\n"; - } - return $comments; + preg_match_all('#//(.*?)$#m', $code, $lineMatches); + preg_match('#/\*(.*?)\*/#ms', $code, $blockMatch); + $lineComments = implode("\n", $lineMatches[1] ?? []); + $blockComments = $blockMatch[1] ?? ''; + + return $lineComments . "\n" . $blockComments . "\n"; } } diff --git a/src/Codeception/Lib/PauseShell.php b/src/Codeception/Lib/PauseShell.php index 3bd5afd567..6e5f750efa 100644 --- a/src/Codeception/Lib/PauseShell.php +++ b/src/Codeception/Lib/PauseShell.php @@ -1,9 +1,11 @@ psyConf = new Configuration([ 'prompt' => '>> ', - 'startupMessage' => "Execution PAUSED All commands will be saved to $relativeLogFilePath" + 'startupMessage' => "Execution PAUSED All commands will be saved to $relativeLogFilePath", + 'historyFile' => codecept_output_dir(self::LOG_FILE), + 'historySize' => 1000, ]); - $this->psyConf->setHistoryFile(codecept_output_dir(self::LOG_FILE)); - $this->psyConf->setHistorySize(1000); } public function addMessage(string $message): self From 7f3afc6d335f7d2b50b8f607b9af5e1dff421ef0 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Thu, 14 Nov 2024 16:53:18 +0100 Subject: [PATCH 17/25] Update RunProcess.php: Docs (minor) --- ext/RunProcess.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ext/RunProcess.php b/ext/RunProcess.php index 7883e7b7d6..15cbe827f5 100644 --- a/ext/RunProcess.php +++ b/ext/RunProcess.php @@ -17,12 +17,12 @@ /** * Extension to start and stop processes per suite. - * Can be used to start/stop selenium server, chromedriver, mailcatcher, etc. + * Can be used to start/stop selenium server, chromedriver, [MailCatcher](https://mailcatcher.me/), etc. * - * Can be configured in suite config: + * Can be enabled in suite config: * * ```yaml - * # acceptance.suite.yml + * # Acceptance.suite.yml * extensions: * enabled: * - Codeception\Extension\RunProcess: @@ -32,8 +32,7 @@ * Multiple parameters can be passed as array: * * ```yaml - * # acceptance.suite.yml - * + * # Acceptance.suite.yml * extensions: * enabled: * - Codeception\Extension\RunProcess: @@ -43,8 +42,7 @@ * * In the end of a suite all launched processes will be stopped. * - * To wait for the process to be launched use `sleep` option. - * In this case you need configuration to be specified as object: + * To wait for the process to be launched use `sleep` option. In this case you need configuration to be specified as object: * * ```yaml * extensions: @@ -55,7 +53,7 @@ * sleep: 5 # wait 5 seconds for processes to boot * ``` * - * HINT: you can use different configurations per environment. + * HINT: You can use different configurations per environment. */ class RunProcess extends Extension { From 3e57474ac5de6659f435584b919600b08551170d Mon Sep 17 00:00:00 2001 From: Dieter Beck Date: Fri, 29 Nov 2024 19:02:09 +0100 Subject: [PATCH 18/25] PHP 8.4: `E_STRICT` deprecation (#6802) * Test against PHP 8.4 * Ignore E_STRICT for PHP 8.4 and higher because it is deprecated See https://wiki.php.net/rfc/deprecations_php_8_4#remove_e_strict_error_level_and_deprecate_e_strict_constant * Remove E_STRICT from default error level because it is deprecated in PHP 8.4 * Remove E_STRICT from internal test suite because it is deprecated in PHP 8.4 --- .github/workflows/build.yml | 6 ++++-- src/Codeception/Configuration.php | 2 +- src/Codeception/Subscriber/ErrorHandler.php | 11 ++++++++--- tests/unit.suite.yml | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba74f363cf..4d923fd215 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,13 +46,15 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - php: ['8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3', '8.4'] mode: ['stable', 'experimental'] exclude: - php: '8.2' mode: 'experimental' - php: '8.3' mode: 'experimental' + - php: '8.4' + mode: 'experimental' steps: - name: Checkout @@ -138,7 +140,7 @@ jobs: fail-fast: false matrix: os: [windows-latest] - php: ['8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3', '8.4'] steps: - name: Checkout diff --git a/src/Codeception/Configuration.php b/src/Codeception/Configuration.php index 972360c194..49ecfed6a5 100644 --- a/src/Codeception/Configuration.php +++ b/src/Codeception/Configuration.php @@ -132,7 +132,7 @@ class Configuration 'enabled' => [], 'config' => [], ], - 'error_level' => 'E_ALL & ~E_STRICT & ~E_DEPRECATED', + 'error_level' => 'E_ALL & ~E_DEPRECATED', 'convert_deprecations_to_exceptions' => false, ]; diff --git a/src/Codeception/Subscriber/ErrorHandler.php b/src/Codeception/Subscriber/ErrorHandler.php index 127eea0b15..5b13d5d141 100644 --- a/src/Codeception/Subscriber/ErrorHandler.php +++ b/src/Codeception/Subscriber/ErrorHandler.php @@ -71,7 +71,12 @@ class ErrorHandler implements EventSubscriberInterface public function __construct() { - $this->errorLevel = E_ALL & ~E_STRICT & ~E_DEPRECATED; + // E_STRICT is deprecated in PHP 8.4 + if (\PHP_VERSION_ID < 80400) { + $this->errorLevel = E_ALL & ~E_STRICT & ~E_DEPRECATED; + } else { + $this->errorLevel = E_ALL & ~E_DEPRECATED; + } } public function onFinish(SuiteEvent $event): void @@ -121,7 +126,7 @@ public function errorHandler(int $errNum, string $errMsg, string $errFile, int $ if (PHPUnitVersion::series() < 10) { throw match ($errNum) { E_DEPRECATED, E_USER_DEPRECATED => new PHPUnit9Deprecation($errMsg, $errNum, $errFile, $errLine), - E_NOTICE, E_STRICT, E_USER_NOTICE => new PHPUnit9Notice($errMsg, $errNum, $errFile, $errLine), + E_NOTICE, E_USER_NOTICE => new PHPUnit9Notice($errMsg, $errNum, $errFile, $errLine), E_WARNING, E_USER_WARNING => new PHPUnit9Warning($errMsg, $errNum, $errFile, $errLine), default => new PHPUnit9Error($errMsg, $errNum, $errFile, $errLine), }; @@ -129,7 +134,7 @@ public function errorHandler(int $errNum, string $errMsg, string $errFile, int $ $errMsg .= ' at ' . $errFile . ':' . $errLine; throw match ($errNum) { E_DEPRECATED, E_USER_DEPRECATED => new Deprecation($errMsg, $errNum, $errFile, $errLine), - E_NOTICE, E_STRICT, E_USER_NOTICE => new Notice($errMsg, $errNum, $errFile, $errLine), + E_NOTICE, E_USER_NOTICE => new Notice($errMsg, $errNum, $errFile, $errLine), E_WARNING, E_USER_WARNING => new Warning($errMsg, $errNum, $errFile, $errLine), default => new Error($errMsg, $errNum, $errFile, $errLine), }; diff --git a/tests/unit.suite.yml b/tests/unit.suite.yml index 85e49456e7..36561b2fff 100644 --- a/tests/unit.suite.yml +++ b/tests/unit.suite.yml @@ -1,7 +1,7 @@ # Codeception Test Suite Configuration # suite for unit (internal) tests. -error_level: "E_ALL | E_STRICT" +error_level: "E_ALL" actor: CodeGuy bootstrap: _bootstrap.php modules: From 092fa32bcd5593938a39795ea0c693c85a79b7f3 Mon Sep 17 00:00:00 2001 From: Igor at KAGG Design Date: Sun, 8 Dec 2024 20:39:34 +0300 Subject: [PATCH 19/25] Fix PHP 8.4 deprecation. (#6811) --- src/Codeception/Lib/Di.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codeception/Lib/Di.php b/src/Codeception/Lib/Di.php index a98277b3b4..8fbbac379c 100644 --- a/src/Codeception/Lib/Di.php +++ b/src/Codeception/Lib/Di.php @@ -120,7 +120,7 @@ public function injectDependencies(object $object, string $injectMethodName = se } } - protected function prepareArgs(ReflectionMethod $method = null, array $defaults = []): array + protected function prepareArgs(?ReflectionMethod $method = null, array $defaults = []): array { $args = []; From 9674e1100960897fc56fb053edd27e66f2e7f2bf Mon Sep 17 00:00:00 2001 From: Dieter Beck Date: Sun, 8 Dec 2024 18:39:46 +0100 Subject: [PATCH 20/25] Test suite names are uppercased (#6813) --- src/Codeception/Template/Bootstrap.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Codeception/Template/Bootstrap.php b/src/Codeception/Template/Bootstrap.php index ed4f247b91..c5d4a7b151 100644 --- a/src/Codeception/Template/Bootstrap.php +++ b/src/Codeception/Template/Bootstrap.php @@ -67,10 +67,10 @@ public function setup(): void $this->say(); $this->say("Next steps:"); - $this->say('1. Edit tests/acceptance.suite.yml to set url of your application. Change PhpBrowser to WebDriver to enable browser testing'); - $this->say("2. Edit tests/functional.suite.yml to enable a framework module. Remove this file if you don't use a framework"); - $this->say("3. Create your first acceptance tests using codecept g:cest acceptance First"); - $this->say("4. Write first test in tests/acceptance/FirstCest.php"); + $this->say('1. Edit tests/Acceptance.suite.yml to set url of your application. Change PhpBrowser to WebDriver to enable browser testing'); + $this->say("2. Edit tests/Functional.suite.yml to enable a framework module. Remove this file if you don't use a framework"); + $this->say("3. Create your first acceptance tests using codecept g:cest Acceptance First"); + $this->say("4. Write first test in tests/Acceptance/FirstCest.php"); $this->say("5. Run tests using: codecept run"); } From 0ecc7668abf1e202d9d967ff28818e09326e74cf Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sun, 8 Dec 2024 18:40:21 +0100 Subject: [PATCH 21/25] Update RunProcess.php: Docs: Executed only once (#6810) --- ext/RunProcess.php | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/RunProcess.php b/ext/RunProcess.php index 15cbe827f5..b6ba20061f 100644 --- a/ext/RunProcess.php +++ b/ext/RunProcess.php @@ -18,6 +18,7 @@ /** * Extension to start and stop processes per suite. * Can be used to start/stop selenium server, chromedriver, [MailCatcher](https://mailcatcher.me/), etc. + * Each command is executed only once, at the beginning of the test suite. To execute a command before each test, see [Before/After Attributes](https://codeception.com/docs/AdvancedUsage#BeforeAfter-Attributes). * * Can be enabled in suite config: * From 58dde9cc70baf9f93d79ca5e8ba43698ffd4152d Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sun, 8 Dec 2024 18:40:47 +0100 Subject: [PATCH 22/25] Update RunFailed.php: Docs (minor) (#6806) --- ext/RunFailed.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/RunFailed.php b/ext/RunFailed.php index 5380edb04e..bf7765b7ac 100644 --- a/ext/RunFailed.php +++ b/ext/RunFailed.php @@ -20,7 +20,7 @@ use function unlink; /** - * Saves failed tests into tests/_output/failed in order to rerun failed tests. + * Saves failed tests into `tests/_output/failed` in order to rerun failed tests. * * To rerun failed tests just run the `failed` group: * @@ -32,9 +32,9 @@ * ``` * --override "extensions: config: Codeception\Extension\RunFailed: fail-group: another_group1" * ``` - * Remember: if you run tests and they generated custom-named fail group, to run this group, you should add override too + * Remember: If you run tests and they generated custom-named fail group, to run this group, you should add override too * - * Starting from Codeception 2.1 **this extension is enabled by default**. + * **This extension is enabled by default.** * * ``` yaml * extensions: From a4a1ee0c29311f7a8f569efb7da4b03c6c14efa7 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sun, 8 Dec 2024 18:41:13 +0100 Subject: [PATCH 23/25] Update Recorder.php: Docs (minor) (#6805) * Update Recorder.php: Docs (minor) --- ext/Recorder.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ext/Recorder.php b/ext/Recorder.php index 195a7e5bfa..0ddbd1533f 100644 --- a/ext/Recorder.php +++ b/ext/Recorder.php @@ -49,14 +49,14 @@ use function uniqid; /** - * Saves a screenshot of each step in acceptance tests and shows them as a slideshow on one HTML page (here's an [example](https://codeception.com/images/recorder.gif)) - * Activated only for suites with WebDriver module enabled. + * Saves a screenshot of each step in acceptance tests and shows them as a slideshow on one HTML page (here's an [example](https://codeception.com/images/recorder.gif)). + * Works only for suites with WebDriver module enabled. * * The screenshots are saved to `tests/_output/record_*` directories, open `index.html` to see them as a slideshow. * * #### Installation * - * Add this to the list of enabled extensions in `codeception.yml` or `acceptance.suite.yml`: + * Add this to the list of enabled extensions in `codeception.yml` or `Acceptance.suite.yml`: * * ``` yaml * extensions: @@ -87,7 +87,7 @@ * ``` * #### Skipping recording of steps with annotations * - * It is also possible to skip recording of steps for specified tests by using the @skipRecording annotation. + * It is also possible to skip recording of steps for specified tests by using the `@skipRecording` annotation. * * ```php * /** @@ -100,7 +100,6 @@ * $I->amOnUrl('https://codeception.com'); * } * ``` - * */ class Recorder extends Extension { From 1caa18d1a1ca4d3c36a0c88b5463a414182f7cf3 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sun, 8 Dec 2024 18:41:30 +0100 Subject: [PATCH 24/25] Update DotReporter.php: Typo (#6804) --- ext/DotReporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/DotReporter.php b/ext/DotReporter.php index f048ae49af..53d9ac3518 100644 --- a/ext/DotReporter.php +++ b/ext/DotReporter.php @@ -13,7 +13,7 @@ /** * DotReporter provides less verbose output for test execution. - * Like PHPUnit printer it prints dots "." for successful testes and "F" for failures. + * Like PHPUnit printer it prints dots "." for successful tests and "F" for failures. * * ![](https://cloud.githubusercontent.com/assets/220264/26132800/4d23f336-3aab-11e7-81ba-2896a4c623d2.png) * From 5f74784541891f054a3f17dcb87a94b3f2552e0d Mon Sep 17 00:00:00 2001 From: Gustavo Nieves Date: Sat, 15 Feb 2025 11:11:55 -0500 Subject: [PATCH 25/25] 5.2.0 --- CHANGELOG-5.x.md | 16 ++++++++++++++++ src/Codeception/Codecept.php | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-5.x.md b/CHANGELOG-5.x.md index ba2aa54d13..745631fbc6 100644 --- a/CHANGELOG-5.x.md +++ b/CHANGELOG-5.x.md @@ -1,3 +1,19 @@ +#### 5.2.0 + +* Fix FAIL message color highlighting by @antonvolokha in #6754 +* Update the codebase to PHP 8.1 by @TavoNiievez in #6747 +* generate:cest: Adding `declare(strict_types=1);` and return type `void` to generated files by @ThomasLandauer in #6736 +* Declare nullable parameter types explicitly by @W0rma in #6774 , #6775 +* chore: Included githubactions in the dependabot config (#6471) by @SamMousa in #6783 +* Added new option --disable-coverage-php to skip coverage.serialized report by @adrenalinkin in #6761 +* chore: add branch alias for main to fix composer install with dev deps by @SamMousa in #6787 +* chore(ci): prevent test CI running twice on PR branches by @SamMousa in #6788 +* Simplify classes by @TavoNiievez in #6767 , #6750 , #6764 +* PHP 8.4: `E_STRICT` deprecation by @W0rma in #6802 +* Fix PHP 8.4 deprecation. by @kagg-design in #6811 +* Fix test suite names in bootstrap command by @W0rma in #6813 +* Docs (minor) by @ThomasLandauer in #6804 , #6805 , #6806 , 6807 , #6792 , #6810 , #6751 , #6744 + #### 5.1.2 * Prevent unrelated error from being displayed if a scenario step has failed by @craig-mcmahon in #6743 diff --git a/src/Codeception/Codecept.php b/src/Codeception/Codecept.php index 46dd63ab09..1410893291 100644 --- a/src/Codeception/Codecept.php +++ b/src/Codeception/Codecept.php @@ -36,7 +36,7 @@ class Codecept /** * @var string */ - public const VERSION = '5.1.2'; + public const VERSION = '5.2.0'; protected ResultAggregator $resultAggregator;