diff --git a/composer.json b/composer.json index df1cabe1..935fcb4c 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "symplify/phpstan-extensions": "^11.1", "symplify/easy-coding-standard": "^11.2", "symplify/rule-doc-generator": "^11.2", - "rector/phpstan-rules": "^0.6.5", + "rector/phpstan-rules": "^0.6", "phpstan/extension-installer": "^1.2", "phpstan/phpstan-strict-rules": "^1.4.5", "phpstan/phpstan-webmozart-assert": "^1.2.2", diff --git a/ecs.php b/ecs.php index 05b44fa8..0f3672ff 100644 --- a/ecs.php +++ b/ecs.php @@ -16,7 +16,7 @@ __DIR__ . '/rector.php', ]); - $ecsConfig->skip(['*/Source/*', '*/Fixture/*']); + $ecsConfig->skip(['*/Source/*', '*/Fixture/*', '*/Expected/*']); $ecsConfig->lineEnding("\n"); }; diff --git a/phpstan.neon b/phpstan.neon index 489366a2..c2828a0e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -13,6 +13,7 @@ parameters: - */Source/* - *Source/* - */Fixture/* + - */Expected/* reportUnmatchedIgnoredErrors: false @@ -30,10 +31,3 @@ parameters: - '#Parameter \#1 \$node (.*?) of method Rector\\(.*?)\(\) should be contravariant with parameter \$node \(PhpParser\\Node\) of method Rector\\Core\\Contract\\Rector\\PhpRectorInterface\:\:refactor\(\)#' - '#Cognitive complexity for "Rector\\PHPUnit\\Rector\\MethodCall\\DelegateExceptionArgumentsRector\:\:refactor\(\)" is 12, keep it under 10#' - - '#Class "Rector\\PHPUnit\\Rector\\MethodCall\\DelegateExceptionArgumentsRector" has invalid namespace category "MethodCall"\. Pick one of\: "StmtsAwareInterface"#' - - '#Parameter \#2 \$callable of method Rector\\Core\\NodeManipulator\\ForeachManipulator\:\:matchOnlyStmt\(\) expects callable\(PhpParser\\Node, PhpParser\\Node\\Stmt\\Foreach_\=\)\: PhpParser\\Node\|null, Closure\(PhpParser\\Node, PhpParser\\Node\\Stmt\\Foreach_\)\: PhpParser\\Node\\Expr\\MethodCall\|PhpParser\\Node\\Expr\\StaticCall\|null given#' - - # solve later -# - '#Call to an undefined method PhpParser\\Node\\Expr\:\:getArgs\(\)#' - - '#Cognitive complexity for "Rector\\PHPUnit\\Rector\\ClassMethod\\CreateMockToAnonymousClassRector\:\:refactor\(\)" is \d+, keep it under 10#' - diff --git a/rector.php b/rector.php index 1a58d92a..7c6d7eee 100644 --- a/rector.php +++ b/rector.php @@ -4,6 +4,7 @@ use Rector\Config\RectorConfig; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; +use Rector\PHPUnit\Set\PHPUnitSetList; use Rector\Set\ValueObject\LevelSetList; use Rector\Set\ValueObject\SetList; @@ -16,6 +17,7 @@ // for tests '*/Source/*', '*/Fixture/*', + '*/Expected/*', // object types StringClassNameToClassConstantRector::class => [ @@ -33,7 +35,7 @@ __DIR__ . '/config/config.php', LevelSetList::UP_TO_PHP_81, SetList::DEAD_CODE, - \Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_100, + PHPUnitSetList::PHPUNIT_100, SetList::CODE_QUALITY, SetList::CODING_STYLE, SetList::EARLY_RETURN, diff --git a/src/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector.php b/src/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector.php new file mode 100644 index 00000000..f7567881 --- /dev/null +++ b/src/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector.php @@ -0,0 +1,108 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->isAbstract()) { + return null; + } + + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + if (! $node->name instanceof Identifier) { + return null; + } + + if (! $this->isName($node->name, '*Test')) { + return null; + } + + // rename class + $testCaseClassName = $node->name->toString() . 'Case'; + $node->name = new Identifier($testCaseClassName); + + $this->printNewNodes($node); + + return $node; + } + + private function printNewNodes(Class_ $class): void + { + $filePath = $this->file->getFilePath(); + + $parentNode = $class->getAttribute(AttributeKey::PARENT_NODE); + if (! $parentNode instanceof Namespace_) { + throw new ShouldNotHappenException(); + } + + $this->neighbourClassLikePrinter->printClassLike($class, $parentNode, $filePath, $this->file); + } +} diff --git a/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/Expected/ExtendsTestCase.php b/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/Expected/ExtendsTestCase.php new file mode 100644 index 00000000..ff1affe1 --- /dev/null +++ b/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/Expected/ExtendsTestCase.php @@ -0,0 +1,7 @@ + +----- + diff --git a/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/Fixture/skip_abstract_class_without_suffix.php.inc b/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/Fixture/skip_abstract_class_without_suffix.php.inc new file mode 100644 index 00000000..093a1e13 --- /dev/null +++ b/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/Fixture/skip_abstract_class_without_suffix.php.inc @@ -0,0 +1,8 @@ +doTestFile(__DIR__ . '/Fixture/skip_abstract_class_without_suffix.php.inc'); + } + + public function testChanges(): void + { + $this->doTestFile(__DIR__ . '/Fixture/extends_test.php.inc'); + + $this->assertFileWasAdded( + __DIR__ . '/Fixture/ExtendsTestCase.php', + FileSystem::read(__DIR__ . '/Expected/ExtendsTestCase.php') + ); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/config/configured_rule.php b/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/config/configured_rule.php new file mode 100644 index 00000000..58647b9c --- /dev/null +++ b/tests/Rector/ClassLike/RemoveTestSuffixFromAbstractTestClassesRector/config/configured_rule.php @@ -0,0 +1,12 @@ +import(__DIR__ . '/../../../../../config/config.php'); + + $rectorConfig->rule(RemoveTestSuffixFromAbstractTestClassesRector::class); +};