Skip to content

Commit eb40dce

Browse files
committed
[behat] Add BehatPHPUnitAssertToWebmozzartRector
1 parent 18d0105 commit eb40dce

File tree

5 files changed

+286
-0
lines changed

5 files changed

+286
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\BehatPHPUnitAssertToWebmozzartRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class BehatPHPUnitAssertToWebmozzartRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\BehatPHPUnitAssertToWebmozzartRector\Fixture;
4+
5+
use Behat\Behat\Context\Context;
6+
use PHPUnit\Framework\Assert;
7+
8+
final class SomeClass implements Context
9+
{
10+
public function someDefinition()
11+
{
12+
Assert::assertSame('expected', 'given');
13+
14+
Assert::assertStringContainsString('needle', 'haystack');
15+
}
16+
}
17+
18+
?>
19+
-----
20+
<?php
21+
22+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\BehatPHPUnitAssertToWebmozzartRector\Fixture;
23+
24+
use Behat\Behat\Context\Context;
25+
use PHPUnit\Framework\Assert;
26+
27+
final class SomeClass implements Context
28+
{
29+
public function someDefinition()
30+
{
31+
\Webmozart\Assert\Assert::same('given', 'expected');
32+
33+
\Webmozart\Assert\Assert::contains('needle', 'haystack');
34+
}
35+
}
36+
37+
?>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\BehatPHPUnitAssertToWebmozzartRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([BehatPHPUnitAssertToWebmozzartRector::class]);
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\CodeQuality\Rector\ClassMethod;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\StaticCall;
9+
use PhpParser\Node\Identifier;
10+
use PhpParser\Node\Name\FullyQualified;
11+
use PhpParser\Node\Stmt\ClassMethod;
12+
use Rector\PHPStan\ScopeFetcher;
13+
use Rector\PHPUnit\Enum\BehatClassName;
14+
use Rector\PHPUnit\Enum\PHPUnitClassName;
15+
use Rector\PHPUnit\Enum\WebmozartClassName;
16+
use Rector\Rector\AbstractRector;
17+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
18+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
19+
20+
/**
21+
* @see \Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\BehatPHPUnitAssertToWebmozzartRector\BehatPHPUnitAssertToWebmozzartRectorTest
22+
*/
23+
final class BehatPHPUnitAssertToWebmozzartRector extends AbstractRector
24+
{
25+
/**
26+
* @var array<string, string>
27+
*/
28+
private const array PHPUNIT_TO_WEBMOZZART_METHODS = [
29+
// Boolean
30+
'assertTrue' => 'true',
31+
'assertFalse' => 'false',
32+
33+
// Null / empty
34+
'assertNull' => 'null',
35+
'assertNotNull' => 'notNull',
36+
'assertEmpty' => 'isEmpty',
37+
'assertNotEmpty' => 'notEmpty',
38+
39+
// Type checks
40+
'assertIsString' => 'string',
41+
'assertIsInt' => 'integer',
42+
'assertIsFloat' => 'float',
43+
'assertIsBool' => 'boolean',
44+
'assertIsArray' => 'isArray',
45+
'assertIsObject' => 'object',
46+
'assertIsCallable' => 'isCallable',
47+
'assertIsResource' => 'resource',
48+
'assertIsIterable' => 'isIterable',
49+
'assertInstanceOf' => 'isInstanceOf',
50+
51+
// Comparison / equality
52+
'assertSame' => 'same',
53+
'assertNotSame' => 'notSame',
54+
'assertEquals' => 'eq',
55+
'assertNotEquals' => 'notEq',
56+
'assertGreaterThan' => 'greaterThan',
57+
'assertGreaterThanOrEqual' => 'greaterThanEq',
58+
'assertLessThan' => 'lessThan',
59+
'assertLessThanOrEqual' => 'lessThanEq',
60+
61+
// Strings
62+
'assertStringContainsString' => 'contains',
63+
'assertStringNotContainsString' => 'notContains',
64+
'assertStringStartsWith' => 'startsWith',
65+
'assertStringStartsNotWith' => 'notStartsWith',
66+
'assertStringEndsWith' => 'endsWith',
67+
'assertStringEndsNotWith' => 'notEndsWith',
68+
'assertMatchesRegularExpression' => 'regex',
69+
70+
// Arrays / count
71+
'assertCount' => 'count',
72+
'assertArrayHasKey' => 'keyExists',
73+
'assertArrayNotHasKey' => 'keyNotExists',
74+
75+
// Misc / less direct
76+
'assertFileExists' => 'fileExists',
77+
'assertFileIsReadable' => 'readable',
78+
'assertDirectoryExists' => 'directory',
79+
];
80+
81+
/**
82+
* @var string[]
83+
*/
84+
private const array FLIPPED_ARGS = [
85+
'assertSame',
86+
'assertNotSame',
87+
'assertEquals',
88+
'assertNotEquals',
89+
'assertGreaterThan',
90+
'assertGreaterThanOrEqual',
91+
'assertLessThan',
92+
'assertLessThanOrEqual',
93+
'assertCount',
94+
'assertContains',
95+
'assertNotContains',
96+
];
97+
98+
public function getRuleDefinition(): RuleDefinition
99+
{
100+
101+
return new RuleDefinition(
102+
'Change PHPUnit assert in Behat context files to Webmozart Assert, as first require a TestCase instance',
103+
[
104+
new CodeSample(
105+
<<<'CODE_SAMPLE'
106+
use Behat\Behat\Context\Context;
107+
use PHPUnit\Framework\Assert;
108+
109+
final class SomeContext implements Context
110+
{
111+
public function someMethod()
112+
{
113+
Assert::assertSame('expected', 'actual');
114+
}
115+
}
116+
CODE_SAMPLE
117+
,
118+
<<<'CODE_SAMPLE'
119+
use Behat\Behat\Context\Context;
120+
use Webmozart\Assert\Assert;
121+
122+
final class SomeContext implements Context
123+
{
124+
public function someMethod()
125+
{
126+
Assert::same('actual', 'expected');
127+
}
128+
}
129+
CODE_SAMPLE
130+
),
131+
132+
]
133+
);
134+
}
135+
136+
/**
137+
* @return array<class-string>
138+
*/
139+
public function getNodeTypes(): array
140+
{
141+
return [ClassMethod::class];
142+
}
143+
144+
/**
145+
* @param ClassMethod $node
146+
*/
147+
public function refactor(Node $node): ?ClassMethod
148+
{
149+
$scope = ScopeFetcher::fetch($node);
150+
if (! $scope->isInClass()) {
151+
return null;
152+
}
153+
154+
$classReflection = $scope->getClassReflection();
155+
if (! $classReflection->is(BehatClassName::CONTEXT)) {
156+
return null;
157+
}
158+
159+
$hasChanged = false;
160+
161+
$this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged): ?StaticCall {
162+
if (! $node instanceof StaticCall) {
163+
return null;
164+
}
165+
166+
if (! $this->isName($node->class, PHPUnitClassName::ASSERT)) {
167+
return null;
168+
}
169+
170+
$phpunitMethodName = $this->getName($node->name);
171+
if ($phpunitMethodName === null) {
172+
return null;
173+
}
174+
175+
// changed method name
176+
$webmozartMethodName = self::PHPUNIT_TO_WEBMOZZART_METHODS[$phpunitMethodName] ?? null;
177+
if ($webmozartMethodName === null) {
178+
return null;
179+
}
180+
181+
if (in_array($phpunitMethodName, self::FLIPPED_ARGS, true) && count($node->args) >= 2) {
182+
// flip first 2 args
183+
$temp = $node->args[0];
184+
$node->args[0] = $node->args[1];
185+
$node->args[1] = $temp;
186+
}
187+
188+
$node->class = new FullyQualified(WebmozartClassName::ASSERT);
189+
$node->name = new Identifier($webmozartMethodName);
190+
191+
$hasChanged = true;
192+
193+
return $node;
194+
});
195+
196+
if (! $hasChanged) {
197+
return null;
198+
}
199+
200+
return $node;
201+
}
202+
}

src/Enum/WebmozartClassName.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Enum;
6+
7+
final class WebmozartClassName
8+
{
9+
public const string ASSERT = 'Webmozart\Assert\Assert';
10+
}

0 commit comments

Comments
 (0)