Skip to content

Commit 14734c6

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

File tree

5 files changed

+283
-0
lines changed

5 files changed

+283
-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: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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', // careful: empty() semantics
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', // PHPUnit ~9+
49+
'assertInstanceOf' => 'isInstanceOf',
50+
51+
// Comparison / equality
52+
'assertSame' => 'same',
53+
'assertNotSame' => 'notSame',
54+
'assertEquals' => 'eq', // loose ==
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', // name varies slightly
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', // or file()
77+
'assertFileIsReadable' => 'readable',
78+
'assertDirectoryExists' => 'directory',
79+
];
80+
81+
private const array FLIPPED_ARGS = [
82+
'assertSame',
83+
'assertNotSame',
84+
'assertEquals',
85+
'assertNotEquals',
86+
'assertGreaterThan',
87+
'assertGreaterThanOrEqual',
88+
'assertLessThan',
89+
'assertLessThanOrEqual',
90+
'assertCount',
91+
'assertContains',
92+
'assertNotContains',
93+
];
94+
95+
public function getRuleDefinition(): RuleDefinition
96+
{
97+
98+
return new RuleDefinition(
99+
'Change PHPUnit assert in Behat context files to Webmozart Assert, as first require a TestCase instance',
100+
[
101+
new CodeSample(
102+
<<<'CODE_SAMPLE'
103+
use Behat\Behat\Context\Context;
104+
use PHPUnit\Framework\Assert;
105+
106+
final class SomeContext implements Context
107+
{
108+
public function someMethod()
109+
{
110+
Assert::assertSame('expected', 'actual');
111+
}
112+
}
113+
CODE_SAMPLE
114+
,
115+
<<<'CODE_SAMPLE'
116+
use Behat\Behat\Context\Context;
117+
use Webmozart\Assert\Assert;
118+
119+
final class SomeContext implements Context
120+
{
121+
public function someMethod()
122+
{
123+
Assert::same('actual', 'expected');
124+
}
125+
}
126+
CODE_SAMPLE
127+
),
128+
129+
]
130+
);
131+
}
132+
133+
/**
134+
* @return array<class-string>
135+
*/
136+
public function getNodeTypes(): array
137+
{
138+
return [ClassMethod::class];
139+
}
140+
141+
/**
142+
* @param ClassMethod $node
143+
*/
144+
public function refactor(Node $node): ?ClassMethod
145+
{
146+
$scope = ScopeFetcher::fetch($node);
147+
if (! $scope->isInClass()) {
148+
return null;
149+
}
150+
151+
$classReflection = $scope->getClassReflection();
152+
if (! $classReflection->is(BehatClassName::CONTEXT)) {
153+
return null;
154+
}
155+
156+
$hasChanged = false;
157+
158+
$this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged): ?StaticCall {
159+
if (! $node instanceof StaticCall) {
160+
return null;
161+
}
162+
163+
if (! $this->isName($node->class, PHPUnitClassName::ASSERT)) {
164+
return null;
165+
}
166+
167+
$phpunitMethodName = $this->getName($node->name);
168+
if ($phpunitMethodName === null) {
169+
return null;
170+
}
171+
172+
// changed method name
173+
$webmozartMethodName = self::PHPUNIT_TO_WEBMOZZART_METHODS[$phpunitMethodName] ?? null;
174+
if ($webmozartMethodName === null) {
175+
return null;
176+
}
177+
178+
if (in_array($phpunitMethodName, self::FLIPPED_ARGS, true) && count($node->args) >= 2) {
179+
// flip first 2 args
180+
$temp = $node->args[0];
181+
$node->args[0] = $node->args[1];
182+
$node->args[1] = $temp;
183+
}
184+
185+
$node->class = new FullyQualified(WebmozartClassName::ASSERT);
186+
$node->name = new Identifier($webmozartMethodName);
187+
188+
$hasChanged = true;
189+
190+
return $node;
191+
});
192+
193+
if (! $hasChanged) {
194+
return null;
195+
}
196+
197+
return $node;
198+
}
199+
}

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)