diff --git a/composer.json b/composer.json index 508ebde94d25fd800492e59ab43b11d040a8a59e..5e13d57282a62d027874f3051a967c7ce65df7fa 100644 --- a/composer.json +++ b/composer.json @@ -7,10 +7,16 @@ "MIT" ], "autoload": { - "psr-4": { "Spip\\Core\\Testing\\": "src/" } + "psr-4": { + "Spip\\Core\\Testing\\": "src/" + } }, "autoload-dev": { - "psr-4": { "Spip\\Core\\Tests\\": "tests/" } + "psr-4": { + "Spip\\Core\\Tests\\": "tests/", + "Utils\\Rector\\": "utils/rector/src", + "Utils\\Rector\\Tests\\": "utils/rector/tests" + } }, "require": { "php": ">=7.4.0", @@ -24,6 +30,7 @@ "require-dev": { "lolli42/finediff": "^1.0", "phpunit/phpunit": "^8.3 || ^9.4", + "rector/rector": "^0.14.1", "symfony/var-dumper": "^5.4 || ^6" }, "repositories": [ diff --git a/rector.php b/rector.php new file mode 100644 index 0000000000000000000000000000000000000000..abe870d8420b4052b21f196e3ad732641f20bd30 --- /dev/null +++ b/rector.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector; +use Rector\Config\RectorConfig; +use Rector\Set\ValueObject\LevelSetList; +use Rector\Set\ValueObject\SetList; +use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector; +use Utils\Rector\Rector\Set\ValueObject\SpipTestSetList; + +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->paths([ + __DIR__ . '/src', + __DIR__ . '/tests' + ]); + + // register a single rule + // $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class); + + // define sets of rules + $rectorConfig->sets([ + SpipTestSetList::ESSAIS_MIGRATION + ]); + /* + $rectorConfig->sets([ + SetList::CODE_QUALITY, + SetList::CODING_STYLE, + LevelSetList::UP_TO_PHP_74 + ]); + + $rectorConfig->skip([ + EncapsedStringsToSprintfRector::class + ]);*/ +}; diff --git a/utils/rector/config/set/spip_test_essais_migration.php b/utils/rector/config/set/spip_test_essais_migration.php new file mode 100644 index 0000000000000000000000000000000000000000..4ee3eac87009705227e6ddf5db348eab66923892 --- /dev/null +++ b/utils/rector/config/set/spip_test_essais_migration.php @@ -0,0 +1,12 @@ +<?php + +declare (strict_types=1); +namespace RectorPrefix202209; + +use Rector\Config\RectorConfig; +use Utils\Rector\Rector\RefactorSpipTestsEssais; + +return static function (RectorConfig $rectorConfig) : void { + // nothing + $rectorConfig->rule(RefactorSpipTestsEssais::class); +}; diff --git a/utils/rector/src/Rector/RefactorSpipTestsEssais.php b/utils/rector/src/Rector/RefactorSpipTestsEssais.php new file mode 100644 index 0000000000000000000000000000000000000000..73018af1a1c93f52f6b174501b6ac4d5679a4649 --- /dev/null +++ b/utils/rector/src/Rector/RefactorSpipTestsEssais.php @@ -0,0 +1,181 @@ +<?php + +declare(strict_types=1); + +namespace Utils\Rector\Rector; + +use Nette\Utils\Strings; +use PhpParser\Builder\Method as MethodBuilder; +use PhpParser\Builder\Use_ as UseBuilder; +use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; +use PhpParser\Node\Stmt\Namespace_; +use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Function_; +use Rector\Core\NodeManipulator\ClassInsertManipulator; +use Rector\Core\Rector\AbstractRector; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use RectorPrefix202209\Symfony\Component\String\UnicodeString; +use PhpParser\Node\Stmt\Use_; +use Rector\Core\Application\FileSystem\RemovedAndAddedFilesCollector; +use Rector\Core\PhpParser\Node\NodeFactory; + +final class RefactorSpipTestsEssais extends AbstractRector +{ + private string $namespace = 'Spip\\Core\\Tests'; + + /** + * @readonly + * @var \Rector\Core\NodeManipulator\ClassInsertManipulator + */ + private $classInsertManipulator; + + /** + * @readonly + * @var \Rector\Core\Application\FileSystem\RemovedAndAddedFilesCollector + */ + private $removedAndAddedFilesCollector; + + public function __construct(ClassInsertManipulator $classInsertManipulator, RemovedAndAddedFilesCollector $removedAndAddedFilesCollector) + { + $this->classInsertManipulator = $classInsertManipulator; + $this->removedAndAddedFilesCollector = $removedAndAddedFilesCollector; + } + + /** + * @return array<class-string<Node>> + */ + public function getNodeTypes(): array + { + return [Namespace_::class]; + } + + /** + * @param MethodCall $node - we can add "MethodCall" type here, because + * only this node is in "getNodeTypes()" + */ + public function refactor(Node $node): ?Node + { + + $fqdn = $this->getFqdn(); + $newNamespace = $fqdn->slice(0, -1)->toString(); + $this->isChangedInNamespaces[$newNamespace] = \true; + $node->name = new Name($newNamespace); + + $uses = [ + (new UseBuilder('PHPUnit\\Framework\\TestCase', Use_::TYPE_NORMAL))->getNode() + ]; + + $class = $this->createTestClass($fqdn->getLast()); + $class->namespacedName = $node->name; + + $setUpMethod = $this->createPublicStaticMethod('setUpBeforeClass'); + $this->classInsertManipulator->addAsFirstMethod($class, $setUpMethod); + + foreach ($node->stmts as $key => $stmt) { + if ($stmt instanceof Expression) { + $setUpMethod->stmts[] = $stmt; + } elseif ($stmt instanceof Function_) { + $method = $this->createPublicMethod($stmt->name->toString()); + $method->stmts = $stmt->stmts; + $class->stmts[] = $method; + } + } + + $node->stmts = [ + ...$uses, + $class, + ]; + + $this->renameFile($fqdn); + return $node; + + } + + /** + * This method helps other to understand the rule and to generate documentation. + */ + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Move essais/*.php to PHPUnit.', + [ + new CodeSample( + <<<CODESAMPLE + function test_connect_sql_id_table_objet(...\$args) { + return id_table_objet(...\$args); + } + // ... + CODESAMPLE, + <<<CODESAMPLE + class IdTableObjetTest extends TestCase { + function testIdTableObjet(\$expected, ...\$args) { + \$this->expectEquals(\$expected, id_table_objet(...\$args)); + } + // ... + } + CODESAMPLE, + ), + ] + ); + } + + private function getFqdn(): FullyQualified + { + $filePath = $this->file->getFilePath(); + $dirname = basename(\pathinfo($filePath, \PATHINFO_DIRNAME)); + $basename = \pathinfo($filePath, \PATHINFO_FILENAME); + + $dir = new UnicodeString($dirname); + $dir = ucfirst($dir->camel()->toString()); + + $file = new UnicodeString($basename); + $file = ucfirst($file->camel()->toString()) . 'Test'; + + return new FullyQualified($this->namespace . "\\Essais\\$dir\\$file"); + } + + /** @see https://github.com/rectorphp/rector/blob/main/rules/Restoration/Rector/ClassLike/UpdateFileNameByClassNameFileSystemRector.php */ + private function renameFile(FullyQualified $namespace): void { + $filePath = $this->file->getFilePath(); + + $newPath = $namespace->slice(3)->toString(); + + $filePath = $this->file->getFilePath(); + $newFileLocation = \dirname($filePath) . \DIRECTORY_SEPARATOR . $newPath . '.php'; + + $this->removedAndAddedFilesCollector->addMovedFile($this->file, $newFileLocation); + } + + + private function createTestClass(string $name): Class_ + { + $class = new Class_($name); + + $class->extends = new FullyQualified('TestCase'); + + return $class; + } + + private function createPublicStaticMethod($name) + { + $method = new MethodBuilder($name); + $method->makePublic(); + $method->makeStatic(); + $method->setReturnType(new Identifier('void')); + return $method->getNode(); + } + + private function createPublicMethod($name) + { + $method = new MethodBuilder($name); + $method->makePublic(); + $method->setReturnType(new Identifier('void')); + return $method->getNode(); + } +} diff --git a/utils/rector/src/Rector/Set/ValueObject/SpipTestSetList.php b/utils/rector/src/Rector/Set/ValueObject/SpipTestSetList.php new file mode 100644 index 0000000000000000000000000000000000000000..64aaac2f9b2ade53a576022b3a7f13e378736785 --- /dev/null +++ b/utils/rector/src/Rector/Set/ValueObject/SpipTestSetList.php @@ -0,0 +1,13 @@ +<?php + +declare (strict_types=1); +namespace Utils\Rector\Rector\Set\ValueObject; + +use Rector\Set\Contract\SetListInterface; +/** + * @api + */ +final class SpipTestSetList implements SetListInterface +{ + public const ESSAIS_MIGRATION = __DIR__ . '/../../../../config/set/spip_test_essais_migration.php'; +}