diff --git a/.github/workflows/composer-normalize.yml b/.github/workflows/composer-normalize.yml index 560c42b..bb19755 100644 --- a/.github/workflows/composer-normalize.yml +++ b/.github/workflows/composer-normalize.yml @@ -3,19 +3,23 @@ name: normalize composer.json on: push: paths: - - 'composer.json' + - "composer.json" jobs: normalize: + timeout-minutes: 1 runs-on: ubuntu-latest steps: - name: Git checkout uses: actions/checkout@v2 + - name: Validate Composer configuration + run: composer validate --strict + - name: normalize composer.json run: | composer global require ergebnis/composer-normalize - composer normalize + composer normalize --indent-style=space --indent-size=4 --no-check-lock --no-update-lock --no-interaction --ansi - uses: stefanzweifel/git-auto-commit-action@v4.0.0 with: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c02f54b..b5ac991 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -6,40 +6,42 @@ jobs: test: runs-on: ubuntu-latest strategy: - fail-fast: true + fail-fast: false matrix: - php: [7.4, 7.3, 7.2] - dependency-version: [prefer-lowest, prefer-stable] + php: [ 7.4, 7.3 ] + laravel: [ 8.*, 7.* ] - name: P${{ matrix.php }} - ${{ matrix.dependency-version }} + name: P${{ matrix.php }} - L${{ matrix.laravel }} steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: none + tools: composer:v2 + - name: Checkout code - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: Cache dependencies - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.composer/cache - key: dependencies-laravel-php-${{ matrix.php }}-${{ matrix.dependency-version}}-composer-${{ hashFiles('composer.json') }} + key: dependencies-laravel-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + restore-keys: | + dependencies-laravel-php-${{ matrix.php }}-composer- + dependencies-laravel-php- - name: Install dependencies - run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest - - - name: php-cs-test - run: bin/php-cs-test - - - name: php-md-test - run: bin/php-md-test ./src - - - name: php-tlint-test - run: bin/php-tlint-test ./src - - - name: php-insights-test - run: bin/php-insights-test - - - name: php-stan-test - run: bin/php-stan-test - - - name: php-mn-test - run: bin/php-mn-test ./src + run: | + composer require --dev "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update + composer update --prefer-stable --prefer-dist --no-interaction --no-suggest + + - run: bin/php-cs-test + - run: bin/php-mn-test ./src + - run: bin/php-md-test ./src + - run: bin/php-tlint-test ./src + - run: bin/php-insights-test + - run: bin/php-stan-test diff --git a/.gitignore b/.gitignore index 54f7ece..6231b75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ -/vendor -/node_modules +/vendor/ +/node_modules/ composer.lock +/vendor-bin/*/vendor/ +/vendor-bin/*/composer.lock diff --git a/README.md b/README.md index 7065a5e..fce0f00 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,7 @@ ## Installation ```bash -composer require --dev bamarni/composer-bin-plugin:^1.4 jasonmccreary/laravel-test-assertions:^0.4.1 -composer bin ci require --dev elbgoods/ci-test-tools - -yarn add --dev elbgoods/ci-test-tools -``` - -If you experience a `PHP Fatal error: Allowed memory size` error, fix your setup or use: -```bash -COMPOSER_MEMORY_LIMIT=-1 composer require --dev elbgoods/ci-test-tools +composer require --dev bamarni/composer-bin-plugin elbgoods/ci-test-tools ``` ## PHP @@ -26,6 +18,12 @@ COMPOSER_MEMORY_LIMIT=-1 composer require --dev elbgoods/ci-test-tools * **tool:** https://github.com/FriendsOfPHP/PHP-CS-Fixer * **config:** [configs/.php_cs.dist](configs/.php_cs.dist) +#### Installation + +```bash +composer bin php-cs require --dev friendsofphp/php-cs-fixer +``` + #### Usage ```bash @@ -53,7 +51,12 @@ If you want to adjust the default configuration you can use your `composer.json[ ### TLint * **tool:** https://github.com/tightenco/tlint -* **config:** [src/TlintPreset.php](src/TlintPreset.php) + +#### Installation + +```bash +composer bin php-tlint require --dev tightenco/tlint +``` #### Usage @@ -67,7 +70,11 @@ You have to create a `tlint.json` file on your project root level with the follo ```json { - "preset": "\\Elbgoods\\CiTestTools\\TlintPreset" + "preset": "laravel", + "disabled": [ + "NoInlineVarDocs", + "NoParensEmptyInstantiations" + ] } ``` @@ -76,35 +83,45 @@ You have to create a `tlint.json` file on your project root level with the follo * **tool:** https://github.com/phpmd/phpmd * **config:** [configs/phpmd.xml](configs/phpmd.xml) -#### Usage +#### Installation ```bash -vendor/bin/php-md-test +composer bin php-md require --dev phpmd/phpmd ``` -#### Configuration +#### Usage -If you think that a rule should be adjusted/ignored open a PR in [this repo](https://github.com/elbgoods/ci-test-tools) to discuss it. +```bash +vendor/bin/php-md-test +``` ### PHP Insights * **tool:** https://github.com/nunomaduro/phpinsights * **config:** [configs/phpinsights.php](configs/phpinsights.php) -#### Usage +#### Installation ```bash -vendor/bin/php-insights-test +composer bin php-insights require --dev nunomaduro/phpinsights ``` -#### Configuration +#### Usage -If you think that a rule should be adjusted/ignored open a PR in [this repo](https://github.com/elbgoods/ci-test-tools) to discuss it. +```bash +vendor/bin/php-insights-test +``` ### PHPMND * **tool:** https://github.com/povils/phpmnd +#### Installation + +```bash +composer bin php-mn require --dev povils/phpmnd +``` + #### Usage ```bash @@ -116,6 +133,12 @@ vendor/bin/php-mn-test * **tool:** https://github.com/nunomaduro/larastan * **config:** [configs/phpstan.neon.dist](configs/phpstan.neon.dist) +#### Installation + +```bash +composer bin php-stan require --dev nunomaduro/larastan +``` + #### Usage ```bash @@ -128,7 +151,7 @@ You have to create a `phpstan.neon.dist` file on your project root level with th ```neon includes: - - ./vendor/nunomaduro/larastan/extension.neon + - ./vendor-bin/php-stan/vendor/nunomaduro/larastan/extension.neon - ./vendor/elbgoods/ci-test-tools/configs/phpstan.neon.dist parameters: diff --git a/composer.json b/composer.json index d7d03fa..30caf6b 100644 --- a/composer.json +++ b/composer.json @@ -1,56 +1,65 @@ { - "name": "elbgoods/ci-test-tools", - "description": "CI Test Tools used by Elbgoods GmbH", - "keywords": [], - "license": "MIT", - "authors": [ - { - "name": "Tom Witkowski", - "email": "dev.gummibeer@gmail.com", - "homepage": "https://gummibeer.de", - "role": "Developer" - } - ], - "require": { - "php": "^7.2", - "friendsofphp/php-cs-fixer": "^2.16.1", - "illuminate/support": "^6.0 || ^7.0", - "jasonmccreary/laravel-test-assertions": "^0.3", - "nunomaduro/larastan": "^0.5.0", - "nunomaduro/phpinsights": "^1.14", - "orchestra/testbench": "^4.0 || ^5.0", - "pdepend/pdepend": "@stable", - "phpmd/phpmd": "^2.8", - "phpstan/phpstan": "^0.12.8", - "phpunit/phpunit": "^8.0 || ^9.0", - "povils/phpmnd": "^2.2", - "tightenco/tlint": "^5.0.5" - }, - "require-dev": { - "illuminate/database": "^6.0 || ^7.0", - "laravel/framework": "^6.0 || ^7.0", - "spatie/enum": "^2.3" - }, - "autoload": { - "psr-4": { - "Elbgoods\\CiTestTools\\": "src/" + "name": "elbgoods/ci-test-tools", + "description": "CI Test Tools used by Elbgoods GmbH", + "keywords": [], + "license": "MIT", + "authors": [ + { + "name": "Tom Witkowski", + "email": "twitkowski@elbgoods.de", + "homepage": "https://gummibeer.de", + "role": "Developer" + } + ], + "require": { + "php": "^7.3 || ^8.0", + "illuminate/database": "^7.0 || ^8.0", + "illuminate/support": "^7.0 || ^8.0", + "illuminate/testing": "^7.0 || ^8.0", + "phpunit/phpunit": "^9.3" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4" }, - "files": [ - "override/SlevomatCodingStandard/Helpers/SuppressHelper.php" + "suggest": { + "friendsofphp/php-cs-fixer": "vendor/bin/php-cs-test", + "nunomaduro/larastan": "vendor/bin/php-stan-test", + "nunomaduro/phpinsights": "vendor/bin/php-insights-test", + "phpmd/phpmd": "vendor/bin/php-md-test", + "povils/phpmnd": "vendor/bin/php-mn-test", + "tightenco/tlint": "vendor/bin/php-tlint-test" + }, + "autoload": { + "psr-4": { + "Elbgoods\\CiTestTools\\": "src/" + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "bin": [ + "bin/php-cs-fix", + "bin/php-cs-test", + "bin/php-insights-test", + "bin/php-md-test", + "bin/php-mn-test", + "bin/php-stan-test", + "bin/php-tlint-test" ], - "exclude-from-classmap": [ - "vendor/slevomat/coding-standard/SlevomatCodingStandard/Helpers/SuppressHelper.php" - ] - }, - "minimum-stability": "dev", - "prefer-stable": true, - "bin": [ - "bin/php-cs-fix", - "bin/php-cs-test", - "bin/php-insights-test", - "bin/php-md-test", - "bin/php-mn-test", - "bin/php-stan-test", - "bin/php-tlint-test" - ] + "scripts": { + "post-install-cmd": [ + "@composer bin all install --ansi --no-interaction --quiet" + ], + "post-update-cmd": [ + "@composer bin all update --ansi --no-interaction --quiet" + ], + "bin": "echo 'composer install --dev'", + "test": [ + "bin/php-cs-test", + "bin/php-mn-test ./src", + "bin/php-md-test ./src", + "bin/php-tlint-test ./src", + "bin/php-insights-test", + "bin/php-stan-test" + ] + } } diff --git a/configs/phpstan.neon.dist b/configs/phpstan.neon.dist index 5404103..e1caac5 100644 --- a/configs/phpstan.neon.dist +++ b/configs/phpstan.neon.dist @@ -1,6 +1,3 @@ parameters: level: 5 checkMissingIterableValueType: false - ignoreErrors: - - '#Unsafe usage of new static#' - - '#PHPDoc tag @var for variable \$[a-zA-Z0-9]+ has invalid type .+#' diff --git a/phpstan.neon.dist b/phpstan.neon.dist index d4b510e..20f8618 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,9 +1,7 @@ includes: - - ./vendor/nunomaduro/larastan/extension.neon + - ./vendor-bin/php-stan/vendor/nunomaduro/larastan/extension.neon - ./configs/phpstan.neon.dist parameters: paths: - ./src - ignoreErrors: - - '#Call to static method [a-zA-Z0-9_]+\(\) on trait [a-zA-Z\\]+#' diff --git a/src/PHPUnit/Assertions/EnumAssertions.php b/src/PHPUnit/Assertions/EnumAssertions.php deleted file mode 100644 index 0721269..0000000 --- a/src/PHPUnit/Assertions/EnumAssertions.php +++ /dev/null @@ -1,69 +0,0 @@ -isEqual($actual), - $message ?? sprintf('Failed asserting that the value is equal to enum %s with value %s.', get_class($expected), $expected->getValue()) - ); - } - - /** - * @param string $expected - * @param string|int|mixed $actual - * @param string|null $message - */ - public static function assertIsEnumValue(string $expected, $actual, ?string $message = null): void - { - try { - $enum = forward_static_call([$expected, 'make'], $actual); - - PHPUnit::assertInstanceOf($expected, $enum); - } catch (Exception $ex) { - PHPUnit::assertTrue(false, $message ?? $ex->getMessage()); - } - } - - /** - * @param string $expected - * @param string|int|mixed|null $actual - * @param string|null $message - */ - public static function assertIsNullableEnumValue(string $expected, $actual, ?string $message = null): void - { - PHPUnit::assertTrue( - is_null($actual) || is_int($actual) || is_string($actual), - $message ?? 'Failed asserting that the value is type of null|int|string.' - ); - - if ($actual !== null) { - static::assertIsEnumValue($expected, $actual, $message); - } - } -} diff --git a/src/PHPUnit/Assertions/JsonApiResourceAssertions.php b/src/PHPUnit/Assertions/JsonApiResourceAssertions.php index f82ad0a..09b2894 100644 --- a/src/PHPUnit/Assertions/JsonApiResourceAssertions.php +++ b/src/PHPUnit/Assertions/JsonApiResourceAssertions.php @@ -3,39 +3,29 @@ namespace Elbgoods\CiTestTools\PHPUnit\Assertions; use Closure; -use Illuminate\Support\Str; use InvalidArgumentException; use OutOfBoundsException; -use PHPUnit\Framework\Assert as PHPUnit; trait JsonApiResourceAssertions { /** @var Closure[] */ protected $jsonApiResourceAssertions = []; - protected function tearDownJsonApiResourceAssertionsTrait(): void + protected function setUpJsonApiResourceAssertionsTrait(): void { - $this->jsonApiResourceAssertions = []; + $this->registerJsonApiResourceAssertions(); } - public static function assertIsJsonApiResource(array $actual, ?string $type = null, ?int $id = null): void - { - PHPUnit::assertArrayHasKey('id', $actual); - PHPUnit::assertIsInt($actual['id']); - PHPUnit::assertTrue(0 < $actual['id']); - if ($id !== null) { - PHPUnit::assertEquals($id, $actual['id']); - } + abstract protected function registerJsonApiResourceAssertions(): void; - PHPUnit::assertArrayHasKey('type', $actual); - PHPUnit::assertIsString($actual['type']); - if ($type !== null) { - PHPUnit::assertEquals($type, $actual['type']); - } + protected function tearDownJsonApiResourceAssertionsTrait(): void + { + $this->jsonApiResourceAssertions = []; } /** - * @param mixed $actual + * @param string $type + * @param array|mixed $actual * @param mixed ...$params Additional arguments passed to bound callback */ public function assertIsJsonApiResourceOfType(string $type, $actual, ...$params): void @@ -44,8 +34,6 @@ public function assertIsJsonApiResourceOfType(string $type, $actual, ...$params) throw new OutOfBoundsException(sprintf('There is no assertion registered for type "%s".', $type)); } - static::assertIsJsonApiResource($actual, class_exists($type) ? Str::snake(class_basename($type)) : null); - call_user_func($this->jsonApiResourceAssertions[$type], $actual, ...$params); } diff --git a/src/PHPUnit/Assertions/ModelAssertions.php b/src/PHPUnit/Assertions/ModelAssertions.php index 7b228c8..1a0d353 100644 --- a/src/PHPUnit/Assertions/ModelAssertions.php +++ b/src/PHPUnit/Assertions/ModelAssertions.php @@ -2,61 +2,58 @@ namespace Elbgoods\CiTestTools\PHPUnit\Assertions; -use Carbon\Carbon; -use DateTimeInterface; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\DB; +use Illuminate\Testing\Constraints\HasInDatabase; use PHPUnit\Framework\Assert as PHPUnit; trait ModelAssertions { /** - * @param Model $expected - * @param array|Model|mixed $actual - * @param string|null $message + * @param Model|string $table + * @param array $data + * @param string|null $connection + * + * @return void */ - public static function assertEqualsModel(Model $expected, $actual, ?string $message = null): void + public static function assertExists($table, array $data = [], ?string $connection = null): void { - if ( - $expected->exists - && $actual instanceof Model - && $actual->exists - ) { - PHPUnit::assertTrue($expected->is($actual)); + if ($table instanceof Model) { + $model = $table; + + $table = $model->getTable(); + $connection = $model->getConnectionName(); + $data = [ + $model->getKeyName() => $model->getKey(), + ]; } - foreach (array_diff($expected->getFillable(), $expected->getHidden()) as $attribute) { - $expectedValue = $expected->getAttribute($attribute); - - if ($expectedValue instanceof DateTimeInterface) { - PHPUnit::assertTrue( - Carbon::instance($expectedValue)->isSameAs(Carbon::ISO8601, data_get($actual, $attribute)), - $message ?? "Failed to assert that attribute \"{$attribute}\" equals expected value." - ); - } else { - PHPUnit::assertEquals( - $expectedValue, - data_get($actual, $attribute), - $message ?? "Failed to assert that attribute \"{$attribute}\" equals expected value." - ); - } - } + PHPUnit::assertThat( + $table, + new HasInDatabase(DB::connection($connection), $data) + ); } - public static function assertModelEquals(Model $expected, Model $actual, ?string $message = null): void + /** + * @param Model $expected + * @param Model|mixed $actual + */ + public static function assertModelEquals(Model $expected, $actual): void { PHPUnit::assertInstanceOf(get_class($expected), $actual); - static::assertEqualsModel($expected, $actual, $message); + PHPUnit::assertTrue($expected->is($actual)); } /** * @param array|mixed $actual + * @param string $locale */ - public static function assertIsTranslatableString($actual): void + public static function assertIsTranslatableString($actual, string $locale = 'en'): void { PHPUnit::assertIsArray($actual); - PHPUnit::assertArrayHasKey('en', $actual); - PHPUnit::assertIsString($actual['en']); + PHPUnit::assertArrayHasKey($locale, $actual); + PHPUnit::assertIsString($actual[$locale]); foreach ($actual as $translation) { NullableTypeAssertions::assertIsNullableString($translation); } diff --git a/src/PHPUnit/TestCase.php b/src/PHPUnit/TestCase.php index 783778c..e9b1920 100644 --- a/src/PHPUnit/TestCase.php +++ b/src/PHPUnit/TestCase.php @@ -2,23 +2,10 @@ namespace Elbgoods\CiTestTools\PHPUnit; -use Elbgoods\CiTestTools\PHPUnit\Assertions\CommonAssertions; -use Elbgoods\CiTestTools\PHPUnit\Assertions\EnumAssertions; -use Elbgoods\CiTestTools\PHPUnit\Assertions\JsonApiResourceAssertions; -use Elbgoods\CiTestTools\PHPUnit\Assertions\ModelAssertions; -use Elbgoods\CiTestTools\PHPUnit\Assertions\NullableTypeAssertions; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; -use JMac\Testing\Traits\HttpTestAssertions; abstract class TestCase extends BaseTestCase { - use HttpTestAssertions, - EnumAssertions, - ModelAssertions, - NullableTypeAssertions, - JsonApiResourceAssertions, - CommonAssertions; - protected function setUp(): void { parent::setUp(); diff --git a/src/TlintPreset.php b/src/TlintPreset.php deleted file mode 100644 index 8563e00..0000000 --- a/src/TlintPreset.php +++ /dev/null @@ -1,22 +0,0 @@ -