mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-27 21:23:31 +00:00
[PHPUnit] Add AddSeeTestAnnotationRector
This commit is contained in:
parent
e72491feb2
commit
1a0db5e25c
|
@ -1,2 +1,3 @@
|
|||
services:
|
||||
Rector\PHPUnit\Rector\MethodCall\RemoveExpectAnyFromMockRector: ~
|
||||
Rector\PHPUnit\Rector\Class_\AddSeeTestAnnotationRector: ~
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPUnit\Rector\Class_;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwareGenericTagValueNode;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
final class AddSeeTestAnnotationRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
*/
|
||||
private $docBlockManipulator;
|
||||
|
||||
public function __construct(DocBlockManipulator $docBlockManipulator)
|
||||
{
|
||||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Add @see annotation test of the class for faster jump to test. Make it FQN, so it stays in the annotation, not in the PHP source code.',
|
||||
[
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeService
|
||||
{
|
||||
}
|
||||
|
||||
class SomeServiceTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
/**
|
||||
* @see \SomeServiceTest
|
||||
*/
|
||||
class SomeService
|
||||
{
|
||||
}
|
||||
|
||||
class SomeServiceTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($this->shouldSkipClass($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string $className */
|
||||
$className = $this->getName($node);
|
||||
|
||||
$testCaseClassName = $this->resolveTestCaseClassName($className);
|
||||
if ($testCaseClassName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->docBlockManipulator->addTag($node, $this->createSeePhpDocTagNode($testCaseClassName));
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function resolveTestCaseClassName(string $className): ?string
|
||||
{
|
||||
if (class_exists($className . 'Test')) {
|
||||
return $className . 'Test';
|
||||
}
|
||||
|
||||
$shortClassName = Strings::after($className, '\\', -1);
|
||||
$testShortClassName = $shortClassName . 'Test';
|
||||
|
||||
$declaredClasses = get_declared_classes();
|
||||
foreach ($declaredClasses as $declaredClass) {
|
||||
if (Strings::endsWith($declaredClass, '\\' . $testShortClassName)) {
|
||||
return $declaredClass;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function createSeePhpDocTagNode(string $className): PhpDocTagNode
|
||||
{
|
||||
return new PhpDocTagNode('@see', new AttributeAwareGenericTagValueNode('\\' . $className));
|
||||
}
|
||||
|
||||
private function shouldSkipClass(Class_ $class): bool
|
||||
{
|
||||
if ($class->isAnonymous()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$className = $this->getName($class);
|
||||
if ($className === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is a test case
|
||||
if (Strings::endsWith($className, 'Test')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is the @see annotation already added
|
||||
if ($class->getDocComment()) {
|
||||
$docCommentText = $class->getDocComment()->getText();
|
||||
$seeClassPattern = '#@see (.*?)' . preg_quote($className, '#') . 'Test#';
|
||||
|
||||
if (Strings::match($docCommentText, $seeClassPattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector;
|
||||
|
||||
use Rector\PHPUnit\Rector\Class_\AddSeeTestAnnotationRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class AddSeeTestAnnotationRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/fixture.php.inc',
|
||||
__DIR__ . '/Fixture/different_namespace.php.inc',
|
||||
__DIR__ . '/Fixture/add_to_doc_block.php.inc',
|
||||
// skip
|
||||
__DIR__ . '/Fixture/skip_existing.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return AddSeeTestAnnotationRector::class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture;
|
||||
|
||||
/**
|
||||
* This is here
|
||||
*/
|
||||
class AddToDocBlock
|
||||
{
|
||||
}
|
||||
|
||||
class AddToDocBlockTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture;
|
||||
|
||||
/**
|
||||
* This is here
|
||||
* @see \Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture\AddToDocBlockTest
|
||||
*/
|
||||
class AddToDocBlock
|
||||
{
|
||||
}
|
||||
|
||||
class AddToDocBlockTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture;
|
||||
|
||||
/**
|
||||
* This is here
|
||||
*/
|
||||
class DifferentNamespace
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture;
|
||||
|
||||
/**
|
||||
* This is here
|
||||
* @see \Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Source\DifferentNamespaceTest
|
||||
*/
|
||||
class DifferentNamespace
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture;
|
||||
|
||||
class SomeService
|
||||
{
|
||||
}
|
||||
|
||||
class SomeServiceTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture;
|
||||
|
||||
/**
|
||||
* @see \Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture\SomeServiceTest
|
||||
*/
|
||||
class SomeService
|
||||
{
|
||||
}
|
||||
|
||||
class SomeServiceTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture;
|
||||
|
||||
/**
|
||||
* @see \Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Fixture\SomeExistingTest
|
||||
*/
|
||||
class SomeExisting
|
||||
{
|
||||
}
|
||||
|
||||
class SomeExistingTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPUnit\Tests\Rector\Class_\AddSeeTestAnnotationRector\Source;
|
||||
|
||||
class DifferentNamespaceTest
|
||||
{
|
||||
|
||||
}
|
|
@ -75,7 +75,7 @@ final class ConfigurationFactory
|
|||
'code_after',
|
||||
'description',
|
||||
'source',
|
||||
'level',
|
||||
'set',
|
||||
];
|
||||
|
||||
if (count(array_intersect(array_keys($config), $requiredKeys)) === count($requiredKeys)) {
|
||||
|
@ -130,33 +130,33 @@ final class ConfigurationFactory
|
|||
return Strings::after($fqnNodeTypes[0], '\\', -1);
|
||||
}
|
||||
|
||||
private function resolveSetConfig(string $level): ?string
|
||||
private function resolveSetConfig(string $set): ?string
|
||||
{
|
||||
if ($level === '') {
|
||||
if ($set === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fileLevel = sprintf('#^%s(\.yaml)?$#', $level);
|
||||
$fileSet = sprintf('#^%s(\.yaml)?$#', $set);
|
||||
$finder = Finder::create()->files()
|
||||
->in($this->setsDirectory)
|
||||
->name($fileLevel);
|
||||
->name($fileSet);
|
||||
|
||||
/** @var SplFileInfo[] $fileInfos */
|
||||
$fileInfos = iterator_to_array($finder->getIterator());
|
||||
if (count($fileInfos) === 0) {
|
||||
// assume new one is created
|
||||
$match = Strings::match($level, '#\/(?<name>[a-zA-Z_-]+])#');
|
||||
$match = Strings::match($set, '#\/(?<name>[a-zA-Z_-]+])#');
|
||||
if (isset($match['name'])) {
|
||||
return $this->setsDirectory . '/' . $match['name'] . '/' . $level;
|
||||
return $this->setsDirectory . '/' . $match['name'] . '/' . $set;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var SplFileInfo $foundLevelConfigFileInfo */
|
||||
$foundLevelConfigFileInfo = array_pop($fileInfos);
|
||||
/** @var SplFileInfo $foundSetConfigFileInfo */
|
||||
$foundSetConfigFileInfo = array_pop($fileInfos);
|
||||
|
||||
return $foundLevelConfigFileInfo->getRealPath();
|
||||
return $foundSetConfigFileInfo->getRealPath();
|
||||
}
|
||||
|
||||
private function isNodeClassMatch(string $nodeClass, string $nodeType): bool
|
||||
|
|
Loading…
Reference in New Issue
Block a user