diff --git a/README.md b/README.md
index 1d978a0c8..b06f9d6d8 100644
--- a/README.md
+++ b/README.md
@@ -12,55 +12,65 @@ Find unused and duplicated definitions easily – without running Behat test
composer require behastan/behastan --dev
```
-## Features
+## Usage
-## 1. Find duplicated definitions
+```bash
+vendor/bin/behastan analyse tests
+```
+
+
-Some definitions have very similar masks, but even identical contents. Better use a one definitions with exact mask, to make your tests more precise and easier to maintain:
+Do you want to skip some rule? You can:
```bash
-vendor/bin/behastan analyze
+vendor/bin/behastan analyse tests --skip=
```
+
+
+Here are the available rules:
+
+### 1. Find duplicated definitions contents
+
+* identifier: `duplicated-contents`
+
+Some definitions have similar masks, even identical contents. Better use a one definitions with exact mask, to make your tests more precise and easier to maintain:
-## 2. Find unused Behat definitions with static analysis
+### 2. Find duplicate masks
-Behat uses `@When()`, `@Then()` and `@Given()` annotations and their PHP 8 attribute alternatives to define method to be called in `*.feature` files. Sometimes test change and lines from `*.feature` files are deleted. But what about definitions?
+* identifier: `duplicated-masks`
-This command helps you to spot definitions that are no longer needed. Just provide test directory (1 or more) and let it statically compare defined and used masks:
+Same as services, there should be no 2 same definition masks. Make them unique with different behavior, or merge them and use one definition instead.
-```bash
-vendor/bin/behastan unused-definitions tests
-```
+
-↓
+### 3. Find unused Behat definitions with static analysis
-```bash
-Checking static, named and regex masks from 100 *Feature files
-==============================================================
+* identifier: `unused-definitions`
-Found 1036 masks:
+Behat uses `@When()`, `@Then()` and `@Given()` annotations or attributes to define a class method that is called in `*.feature` files. Sometimes test change and lines from `*.feature` files are deleted. But what about definitions?
- * 747 exact
- * 106 /regex/
- * 181 :named
+This rule spots definitions that are no longer needed.
- 1036/1036 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
+
-the product price is :value
-tests/Behat/ProductContext.php
+## Output example
-/^I submit order form and see payment page$/
-tests/Behat/OrderContext.php
+```bash
+Found 127 Context and 225 feature files
+Extracting definitions masks...
+Found 1367 masks:
+ * 863 exact
+ * 204 /regex/
+ * 298 :named
- [ERROR] Found 2 unused definitions
+Running analysis...
```
-You can also add this command to CI, to get instant feedback about unused definitions.
-
+Add this command to CI, to get instant feedback of any changes.
That's it!
diff --git a/rector.php b/rector.php
index 0489ab6d2..ec52a77e3 100644
--- a/rector.php
+++ b/rector.php
@@ -16,7 +16,8 @@
codingStyle: true,
instanceOf: true,
phpunitCodeQuality: true,
- naming: true
+ naming: true,
+ rectorPreset: true,
)
->withImportNames()
->withSkip(['*/scoper.php', '*/Source/*', '*/Fixture/*']);
diff --git a/src/Analyzer/UnusedDefinitionsAnalyzer.php b/src/Analyzer/UnusedDefinitionsAnalyzer.php
index 5cdfaf5ba..43ad773ef 100644
--- a/src/Analyzer/UnusedDefinitionsAnalyzer.php
+++ b/src/Analyzer/UnusedDefinitionsAnalyzer.php
@@ -6,7 +6,6 @@
use Nette\Utils\Strings;
use Rector\Behastan\DefinitionMasksExtractor;
-use Rector\Behastan\Reporting\MaskCollectionStatsPrinter;
use Rector\Behastan\UsedInstructionResolver;
use Rector\Behastan\ValueObject\Mask\AbstractMask;
use Rector\Behastan\ValueObject\Mask\ExactMask;
@@ -32,7 +31,6 @@ public function __construct(
private SymfonyStyle $symfonyStyle,
private UsedInstructionResolver $usedInstructionResolver,
private DefinitionMasksExtractor $definitionMasksExtractor,
- private MaskCollectionStatsPrinter $maskCollectionStatsPrinter,
) {
}
@@ -55,7 +53,6 @@ public function analyse(array $contextFiles, array $featureFiles, MaskCollection
}
$maskCollection = $this->definitionMasksExtractor->extract($contextFiles);
- $this->maskCollectionStatsPrinter->print($maskCollection);
$featureInstructions = $this->usedInstructionResolver->resolveInstructionsFromFeatureFiles($featureFiles);
$maskProgressBar = $this->symfonyStyle->createProgressBar($maskCollection->count());
diff --git a/src/Command/AnalyzeCommand.php b/src/Command/AnalyzeCommand.php
index c22839187..53eeaf54b 100644
--- a/src/Command/AnalyzeCommand.php
+++ b/src/Command/AnalyzeCommand.php
@@ -40,6 +40,8 @@ public function __construct(
protected function configure(): void
{
$this->setName('analyze');
+ $this->setAliases(['analyse']);
+
$this->setDescription('Run complete static analysis on Behat definitions and features');
$this->addArgument(
@@ -65,7 +67,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$contextFileInfos = BehatMetafilesFinder::findContextFiles([$testDirectory]);
if ($contextFileInfos === []) {
$this->symfonyStyle->error(sprintf(
- 'No *.Context files found in "%s". Please provide correct test directory',
+ 'No *.Context files found in "%s". Please provide correct directory',
$testDirectory
));
return self::FAILURE;
@@ -74,12 +76,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$featureFileInfos = BehatMetafilesFinder::findFeatureFiles([$testDirectory]);
if ($featureFileInfos === []) {
$this->symfonyStyle->error(sprintf(
- 'No *.feature files found in "%s". Please provide correct test directory',
+ 'No *.feature files found in "%s". Please provide correct directory',
$testDirectory
));
return self::FAILURE;
}
+ $skips = $input->getOption('skip');
+
$this->symfonyStyle->writeln(sprintf(
'Found %d Context and %d feature files>',
count($contextFileInfos),
@@ -91,21 +95,26 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->symfonyStyle->newLine();
$this->maskCollectionStatsPrinter->print($maskCollection);
-
$this->symfonyStyle->newLine();
- // @todo skip by "--skip" option
-
$this->symfonyStyle->writeln('Running analysis...>');
+ $this->symfonyStyle->newLine();
/** @var RuleError[] $allRuleErrors */
$allRuleErrors = [];
foreach ($this->rules as $rule) {
+ if ($skips !== [] && in_array($rule->getIdentifier(), $skips, true)) {
+ $this->symfonyStyle->writeln(sprintf('Skipping "%s" rule>', $rule->getIdentifier()));
+ $this->symfonyStyle->newLine();
+ continue;
+ }
+
$ruleErrors = $rule->process($contextFileInfos, $featureFileInfos, $maskCollection, $testDirectory);
$allRuleErrors = array_merge($allRuleErrors, $ruleErrors);
}
if ($allRuleErrors === []) {
+ $this->symfonyStyle->newLine(2);
$this->symfonyStyle->success('No errors found. Good job!');
return self::SUCCESS;
diff --git a/src/Contract/RuleInterface.php b/src/Contract/RuleInterface.php
index 8164205e4..8c318b20b 100644
--- a/src/Contract/RuleInterface.php
+++ b/src/Contract/RuleInterface.php
@@ -4,6 +4,7 @@
namespace Rector\Behastan\Contract;
+use Rector\Behastan\Enum\RuleIdentifier;
use Rector\Behastan\ValueObject\MaskCollection;
use Rector\Behastan\ValueObject\RuleError;
use Symfony\Component\Finder\SplFileInfo;
@@ -22,4 +23,9 @@ public function process(
MaskCollection $maskCollection,
string $projectDirectory
): array;
+
+ /**
+ * @return RuleIdentifier::*
+ */
+ public function getIdentifier(): string;
}
diff --git a/src/Enum/RuleIdentifier.php b/src/Enum/RuleIdentifier.php
new file mode 100644
index 000000000..0d8348cac
--- /dev/null
+++ b/src/Enum/RuleIdentifier.php
@@ -0,0 +1,23 @@
+