diff --git a/docs/ForbidPestPhpOnly.md b/docs/ForbidPestPhpOnly.md index e3d83c9..0741079 100644 --- a/docs/ForbidPestPhpOnly.md +++ b/docs/ForbidPestPhpOnly.md @@ -42,5 +42,6 @@ uses()->group('integration'); // ✓ Valid ## Important Notes - The rule considers a file to be a test when it resides inside a `tests/` directory, ends with `Test.php`, or is named `Pest.php` +- Only `->only()` calls chained from Pest's `test()` or `it()` helpers are reported; other methods named `only` are ignored - `only()` is meant for temporary local debugging; remove the call before committing - Pest also provides other granular filters (`--filter`, `--group`, datasets). Prefer those when you need persistent targeting without modifying source files diff --git a/src/Rules/ForbidPestPhpOnly/ForbidPestPhpOnlyRule.php b/src/Rules/ForbidPestPhpOnly/ForbidPestPhpOnlyRule.php index 4bb734a..1b0e339 100644 --- a/src/Rules/ForbidPestPhpOnly/ForbidPestPhpOnlyRule.php +++ b/src/Rules/ForbidPestPhpOnly/ForbidPestPhpOnlyRule.php @@ -3,8 +3,11 @@ namespace Orrison\MeliorStan\Rules\ForbidPestPhpOnly; use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Identifier; +use PhpParser\Node\Name; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; @@ -17,6 +20,11 @@ class ForbidPestPhpOnlyRule implements Rule { public const ERROR_MESSAGE = 'Pest\'s only() filter should not be used in committed tests.'; + private const PEST_ENTRY_POINTS = [ + 'test', + 'it', + ]; + /** * @return class-string */ @@ -42,6 +50,10 @@ public function processNode(Node $node, Scope $scope): array return []; } + if (! $this->originatesFromPestEntry($node)) { + return []; + } + return [ RuleErrorBuilder::message(self::ERROR_MESSAGE) ->identifier('MeliorStan.forbidPestPhpOnly') @@ -49,6 +61,30 @@ public function processNode(Node $node, Scope $scope): array ]; } + protected function originatesFromPestEntry(MethodCall $node): bool + { + /** @var Expr $expression */ + $expression = $node->var; + + while ($expression instanceof MethodCall) { + /** @var MethodCall $innerCall */ + $innerCall = $expression; + $expression = $innerCall->var; + } + + if (! $expression instanceof FuncCall) { + return false; + } + + $name = $expression->name; + + if (! $name instanceof Name) { + return false; + } + + return in_array(strtolower($name->getLast()), self::PEST_ENTRY_POINTS, true); + } + protected function isTestFile(string $filePath): bool { $normalized = str_replace('\\', '/', $filePath); diff --git a/tests/Rules/ForbidPestPhpOnly/Fixture/InvalidPestFixture.php b/tests/Rules/ForbidPestPhpOnly/Fixture/InvalidPestFixture.php index 7c3c508..4a228db 100644 --- a/tests/Rules/ForbidPestPhpOnly/Fixture/InvalidPestFixture.php +++ b/tests/Rules/ForbidPestPhpOnly/Fixture/InvalidPestFixture.php @@ -7,5 +7,3 @@ it('can run another test', function () { expect(true)->toBeTrue(); })->only(); - -uses()->group('integration')->only(); diff --git a/tests/Rules/ForbidPestPhpOnly/Fixture/NonPestOnlyUsageFixture.php b/tests/Rules/ForbidPestPhpOnly/Fixture/NonPestOnlyUsageFixture.php new file mode 100644 index 0000000..1ee3c95 --- /dev/null +++ b/tests/Rules/ForbidPestPhpOnly/Fixture/NonPestOnlyUsageFixture.php @@ -0,0 +1,35 @@ +only(); + $collection->filter()->only(); + } + + public function useProperty(): void + { + $helper = new class () { + public function only(): void {} + }; + + $this->callOnly($helper); + } + + protected function callOnly(object $object): void + { + if (method_exists($object, 'only')) { + $object->only(); + } + } +} diff --git a/tests/Rules/ForbidPestPhpOnly/ForbidPestPhpOnlyRuleTest.php b/tests/Rules/ForbidPestPhpOnly/ForbidPestPhpOnlyRuleTest.php index 5222107..633e1fb 100644 --- a/tests/Rules/ForbidPestPhpOnly/ForbidPestPhpOnlyRuleTest.php +++ b/tests/Rules/ForbidPestPhpOnly/ForbidPestPhpOnlyRuleTest.php @@ -25,7 +25,6 @@ public function testOnlyUsageIsReported(): void ], [ [ForbidPestPhpOnlyRule::ERROR_MESSAGE, 3], [ForbidPestPhpOnlyRule::ERROR_MESSAGE, 7], - [ForbidPestPhpOnlyRule::ERROR_MESSAGE, 11], ]); } @@ -33,6 +32,7 @@ public function testValidUsagePasses(): void { $this->analyse([ __DIR__ . '/Fixture/ValidPestFixture.php', + __DIR__ . '/Fixture/NonPestOnlyUsageFixture.php', ], []); }