diff --git a/.editorconfig b/.editorconfig index 1954582..c9a2a17 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,9 +8,11 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[*.yaml] +[*.{json,yaml,yml}] indent_size = 2 [*.md] -indent_size = 2 trim_trailing_whitespace = false + +[*.neon] +indent_style = tab diff --git a/.env b/.env new file mode 100644 index 0000000..e69de29 diff --git a/.gitattributes b/.gitattributes index 3dbe200..c365f7b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,16 @@ -/.* export-ignore -/tests export-ignore -/phpunit.xml.dist export-ignore -/psalm.xml.dist export-ignore -/README.md export-ignore +/.editorconfig export-ignore +/.env export-ignore +/.gitattributes export-ignore +/.github/ export-ignore +/.gitignore export-ignore +/.gitlab-ci.yml export-ignore +/.gitlab-ci/ export-ignore +/.php-cs-fixer.php export-ignore +/bin/ export-ignore +/compose.yaml export-ignore +/docs/ export-ignore +/phpstan-baseline.neon export-ignore +/phpstan.neon export-ignore +/phpunit.xml.dist export-ignore +/sonar-project.properties export-ignore +/tests/ export-ignore diff --git a/.github/workflows/qa.yaml b/.github/workflows/qa.yaml new file mode 100644 index 0000000..1535374 --- /dev/null +++ b/.github/workflows/qa.yaml @@ -0,0 +1,36 @@ +name: Quality Assurance + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + qa: + name: Quality Checks + runs-on: ubuntu-latest + + steps: + - name: Git Checkout + uses: actions/checkout@v4 + + - name: PHP Setup + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + + - name: Validate composer.json + run: composer validate --strict + + - name: Install dependencies + uses: ramsey/composer-install@v2 + + - name: Check CS-Fixer + run: composer cs:check + + - name: Check PHPStan + run: composer phpstan diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..9dd02e1 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,43 @@ +name: Tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + tests: + name: PHPUnit with PHP ${{ matrix.php-version }} ${{ matrix.dependencies }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + include: + - php-version: "8.1" + dependencies: "lowest" + - php-version: "8.1" + dependencies: "highest" + - php-version: "8.2" + dependencies: "highest" + + steps: + - name: Git Checkout + uses: actions/checkout@v4 + + - name: PHP Setup + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + + - name: Install dependencies + uses: ramsey/composer-install@v2 + with: + dependency-versions: ${{ matrix.dependencies }} + + - name: Execute tests + run: composer tests diff --git a/.gitignore b/.gitignore index fd4ad03..9ab1ab7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,17 @@ /composer.lock /vendor/ +# Docker Compose +/compose.override.yaml + # PHP-CS-Fixer -/php-cs-fixer.cache +/.php-cs-fixer.cache # PHPUnit /.phpunit.result.cache +/.phpunit.cache/ +/reports/ -# Psalm -/.psalm.cache +# Optionally, ignore project files for common IDE +# A better alternative would be to globally ignore configuration files of your preferred IDE (see: https://stackoverflow.com/a/7335487) +#/.idea/ diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 3831bbb..4795d19 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -1,81 +1,39 @@ setFinder((new PhpCsFixer\Finder)->in([ - __DIR__.'/src', - __DIR__.'/tests', - ])) + ->setFinder((new PhpCsFixer\Finder) + ->in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->notPath(['DependencyInjection/Configuration.php', 'app/var']) + ) ->setRiskyAllowed(true) ->setRules([ - // See: https://mlocati.github.io/php-cs-fixer-configurator - '@PHP74Migration' => true, '@Symfony' => true, '@Symfony:risky' => true, - '@DoctrineAnnotation' => true, - '@PHPUnit84Migration:risky' => true, - 'array_push' => true, - 'class_attributes_separation' => ['elements' => ['method' => 'one']], - 'class_definition' => ['single_item_single_line' => true], - 'combine_consecutive_unsets' => true, - 'declare_strict_types' => true, - 'echo_tag_syntax' => ['format' => 'long'], - 'heredoc_indentation' => ['indentation' => 'same_as_start'], - 'heredoc_to_nowdoc' => true, - 'linebreak_after_opening_tag' => true, - 'mb_str_functions' => true, - 'method_chaining_indentation' => true, - 'native_constant_invocation' => [ - 'fix_built_in' => false, - 'include' => [ - 'DIRECTORY_SEPARATOR', - 'PHP_INT_SIZE', - 'PHP_SAPI', - 'PHP_VERSION_ID', - ], - 'scope' => 'namespaced', - 'strict' => true, - ], - 'native_function_invocation' => [ - 'include' => [ - '@compiler_optimized', - ], - 'scope' => 'namespaced', - 'strict' => true, - ], - 'new_with_braces' => false, - 'no_extra_blank_lines' => [ - 'tokens' => [ - 'break', - 'continue', - 'extra', - 'return', - 'throw', - 'use', - 'parenthesis_brace_block', - 'square_brace_block', - 'curly_brace_block', - ], - ], - 'no_null_property_initialization' => true, - 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true, 'remove_inheritdoc' => true], - 'no_unreachable_default_argument_value' => true, - 'no_useless_else' => true, - 'no_useless_return' => true, - 'ordered_class_elements' => true, - 'ordered_traits' => false, - 'php_unit_method_casing' => false, - 'php_unit_strict' => false, - 'php_unit_test_annotation' => ['style' => 'annotation'], - 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], - 'php_unit_test_class_requires_covers' => false, - 'phpdoc_add_missing_param_annotation' => true, - 'phpdoc_order' => true, - 'phpdoc_summary' => false, - 'return_assignment' => true, - 'simple_to_complex_string_variable' => true, - 'simplified_if_return' => true, - 'single_line_throw' => false, - 'strict_comparison' => true, - 'strict_param' => true, - 'visibility_required' => ['elements' => ['property', 'method', 'const']], + + // declare strict types must be on first line after opening tag + 'blank_line_after_opening_tag' => false, // overwrite @Symfony + 'linebreak_after_opening_tag' => false, // overwrite @Symfony + 'declare_strict_types' => true, // custom + + // allow throw's in multiple lines, so message can be a long string + 'single_line_throw' => false, // overwrite @Symfony + + // we want spaces + 'concat_space' => ['spacing' => 'one'], // overwrite @Symfony + + // we want to leave the choice to the developer, + // because some people have their own style of naming test methods + 'php_unit_method_casing' => false, // overwrite @Symfony + + // we want to leave the choice to the developer + 'php_unit_test_annotation' => false, // overwrite @Symfony:risky ]); diff --git a/README.md b/README.md index 042c393..da21a98 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ Provides tools for Pimcore unit/integration testing with PHPUnit. ## Installation -```shell -composer require --dev teamneusta/pimcore-testing-framework -``` +1. **Require the bundle** + + ```shell + composer require --dev teamneusta/pimcore-testing-framework + ``` ## Usage @@ -41,10 +43,11 @@ Pimcore also expects some configuration (e.g., for the [`security`](https://github.com/pimcore/skeleton/blob/10.2/config/packages/security.yaml)) to be present. You can use the `\Neusta\Pimcore\TestingFramework\Kernel\TestKernel` as a base, -which already provides all necessary configurations with default values +which already provides all necessary configurations with default values (see: `dist/config` and `dist/pimcore10/config` or `dist/pimcore11/config`, depending on your Pimcore version). For a basic setup, you can use the `TestKernel` directly: + ```php # tests/bootstrap.php .reports/php-cs-fixer.json", + "cs:fix": "php-cs-fixer fix --ansi --verbose --diff", + "phpstan": "phpstan analyse --ansi", + "phpstan:gitlab-ci": "phpstan analyse --ansi --no-interaction --no-progress --error-format=gitlab > .reports/phpstan.json", + "tests": "phpunit", + "tests:coverage:gitlab-ci": "phpunit --colors=never --coverage-text --coverage-clover .reports/clover.xml --coverage-cobertura .reports/cobertura.xml --log-junit .reports/junit.xml" + }, + "scripts-descriptions": { + "cs:check": "Checks code style (but doesn't fix anything)", + "cs:check:gitlab-ci": "Checks code style and redirects the output into a GitLab readable file", + "cs:fix": "Checks and fixes code style", + "phpstan": "Checks for code smells", + "phpstan:gitlab-ci": "Checks for code smells and redirects the output into a GitLab readable file", + "tests": "Run all phpunit tests", + "tests:coverage:gitlab-ci": "Run all phpunit tests and create coverage and log reports" + } } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..53a1f75 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,56 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#2 \\$registry of class Neusta\\\\Pimcore\\\\TestingFramework\\\\Database\\\\PimcoreDatabaseResetter constructor expects Doctrine\\\\Persistence\\\\ManagerRegistry, object given\\.$#" + count: 1 + path: src/Database/DatabaseResetter.php + + - + message: "#^Method Neusta\\\\Pimcore\\\\TestingFramework\\\\Database\\\\PimcoreInstaller\\:\\:getDataFiles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/Database/PimcoreInstaller.php + + - + message: "#^Method Neusta\\\\Pimcore\\\\TestingFramework\\\\Database\\\\PimcoreInstaller\\:\\:getDataFiles\\(\\) should return array but returns array\\\\|false\\.$#" + count: 1 + path: src/Database/PimcoreInstaller.php + + - + message: "#^PHPDoc tag @throws with type Doctrine\\\\DBAL\\\\DBALException is not subtype of Throwable$#" + count: 1 + path: src/Database/PimcoreInstaller.php + + - + message: "#^Parameter \\#1 \\$sql of method Doctrine\\\\DBAL\\\\Connection\\:\\:exec\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/Database/PimcoreInstaller.php + + - + message: "#^Parameter \\#2 \\$string of function explode expects string, string\\|false given\\.$#" + count: 1 + path: src/Database/PimcoreInstaller.php + + - + message: "#^Property Neusta\\\\Pimcore\\\\TestingFramework\\\\Database\\\\PimcoreInstaller\\:\\:\\$dumpLocation \\(string\\|null\\) does not accept string\\|false\\.$#" + count: 1 + path: src/Database/PimcoreInstaller.php + + - + message: "#^Method Neusta\\\\Pimcore\\\\TestingFramework\\\\Database\\\\RunCommand\\:\\:__invoke\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/Database/RunCommand.php + + - + message: "#^Method Pimcore\\\\Kernel\\:\\:configureContainer\\(\\) invoked with 1 parameter, 3 required\\.$#" + count: 1 + path: src/Kernel/TestKernel.php + + - + message: "#^Parameter \\#1 \\$bundle of method Pimcore\\\\HttpKernel\\\\BundleCollection\\\\BundleCollection\\:\\:addBundle\\(\\) expects string\\|Symfony\\\\Component\\\\HttpKernel\\\\Bundle\\\\BundleInterface, Pimcore\\\\Bundle\\\\AdminBundle\\\\PimcoreAdminBundle given\\.$#" + count: 1 + path: src/Kernel/TestKernel.php + + - + message: "#^Parameter \\#1 \\$name of static method Neusta\\\\Pimcore\\\\TestingFramework\\\\Pimcore\\\\BootstrapPimcore\\:\\:setEnv\\(\\) expects string, int\\|string given\\.$#" + count: 1 + path: src/Pimcore/BootstrapPimcore.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..c7a3b9b --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,18 @@ +includes: + - phpstan-baseline.neon + +parameters: + + level: 8 + + paths: + - src + + excludePaths: + # There are two errors, that were not able to add to the baseline + - src/Kernel/TestKernel.php + + bootstrapFiles: + - vendor/pimcore/pimcore/stubs/dynamic-constants.php + + checkGenericClassInNonGenericObjectType: false diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c7ba0c7..9896a4b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,9 +1,15 @@ - @@ -11,13 +17,15 @@ - - tests - + + + ./tests/ + + - + - src + ./src/ diff --git a/psalm.xml.dist b/psalm.xml.dist deleted file mode 100644 index f629a14..0000000 --- a/psalm.xml.dist +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/Database/PimcoreInstaller.php b/src/Database/PimcoreInstaller.php index 6dd5660..1bc2fae 100644 --- a/src/Database/PimcoreInstaller.php +++ b/src/Database/PimcoreInstaller.php @@ -34,7 +34,7 @@ public function setDumpLocation(string $dumpLocation): void $filesystem = new Filesystem(); $dumpLocation = $filesystem->isAbsolutePath($dumpLocation) ? rtrim($dumpLocation, '/') - : PIMCORE_PROJECT_ROOT.'/'.trim($dumpLocation, '/'); + : PIMCORE_PROJECT_ROOT . '/' . trim($dumpLocation, '/'); if (!$filesystem->exists($dumpLocation)) { throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dumpLocation)); @@ -52,7 +52,7 @@ public function setDumpLocation(string $dumpLocation): void public function insertDatabaseDump($file): void { if (str_ends_with($file, self::GZIP_FILE_EXTENSION)) { - $file = 'compress.zlib://'.$file; + $file = 'compress.zlib://' . $file; } $dumpFile = file_get_contents($file); @@ -69,7 +69,7 @@ public function insertDatabaseDump($file): void foreach ($singleQueries as $m) { $sql = trim($m); if ('' !== $sql) { - $batchQueries[] = $sql.';'; + $batchQueries[] = $sql . ';'; } if (\count($batchQueries) > 500) { @@ -93,6 +93,6 @@ protected function getDataFiles(): array $pattern = sprintf('%s/*{%s}', $this->dumpLocation, implode(',', self::DUMP_FILE_EXTENSIONS)); - return glob($pattern, GLOB_BRACE); + return glob($pattern, \GLOB_BRACE); } } diff --git a/src/Kernel/TestKernel.php b/src/Kernel/TestKernel.php index 0ef66ab..3e5e25e 100644 --- a/src/Kernel/TestKernel.php +++ b/src/Kernel/TestKernel.php @@ -17,13 +17,13 @@ class TestKernel extends Kernel { protected function configureContainer(ContainerConfigurator $container): void { - $container->import(__DIR__.'/../../dist/config/*.yaml'); - $container->import(__DIR__.'/../../dist/pimcore10/config/*.yaml'); + $container->import(__DIR__ . '/../../dist/config/*.yaml'); + $container->import(__DIR__ . '/../../dist/pimcore10/config/*.yaml'); parent::configureContainer($container); - if (file_exists($pimcore10Config = $this->getProjectDir().'/config/pimcore10')) { - $container->import($pimcore10Config.'/*.{php,yaml}'); + if (file_exists($pimcore10Config = $this->getProjectDir() . '/config/pimcore10')) { + $container->import($pimcore10Config . '/*.{php,yaml}'); } } } @@ -35,14 +35,13 @@ protected function configureContainer( LoaderInterface $loader, ContainerBuilder $builder, ): void { - - $container->import(__DIR__.'/../../dist/config/*.yaml'); - $container->import(__DIR__.'/../../dist/pimcore11/config/*.yaml'); + $container->import(__DIR__ . '/../../dist/config/*.yaml'); + $container->import(__DIR__ . '/../../dist/pimcore11/config/*.yaml'); parent::configureContainer($container, $loader, $builder); - if (file_exists($pimcore11Config = $this->getProjectDir().'/config/pimcore11')) { - $container->import($pimcore11Config.'/*.{php,yaml}'); + if (file_exists($pimcore11Config = $this->getProjectDir() . '/config/pimcore11')) { + $container->import($pimcore11Config . '/*.{php,yaml}'); } } diff --git a/src/Pimcore/BootstrapPimcore.php b/src/Pimcore/BootstrapPimcore.php index 9c972bb..5699435 100644 --- a/src/Pimcore/BootstrapPimcore.php +++ b/src/Pimcore/BootstrapPimcore.php @@ -25,6 +25,6 @@ public static function bootstrap(string ...$envVars): void public static function setEnv(string $name, string $value): void { - putenv("{$name}=".$_ENV[$name] = $_SERVER[$name] = $value); + putenv("{$name}=" . $_ENV[$name] = $_SERVER[$name] = $value); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1c272cc..46902d0 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,7 +2,7 @@ declare(strict_types=1); -include dirname(__DIR__).'/vendor/autoload.php'; +include dirname(__DIR__) . '/vendor/autoload.php'; Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');