Skip to content

Commit 6cc9cf2

Browse files
committed
fix: TagFilter should handle tags with or without leading @
Update the filtering to match tags regardless of the gherkin parsing mode.
1 parent d5e1605 commit 6cc9cf2

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

src/Filter/TagFilter.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,18 @@ protected function isTagsMatchCondition(array $tags)
117117
return true;
118118
}
119119

120+
// If the file was parsed in legacy mode, the `@` prefix will have been removed from the individual tags on the
121+
// parsed node. The tags in the filter expression still have their @ so we add the prefix back here if required.
122+
// This can be removed once legacy parsing mode is removed.
123+
$tags = array_map(
124+
static fn (string $tag) => str_starts_with($tag, '@') ? $tag : '@' . $tag,
125+
$tags
126+
);
127+
120128
foreach (explode('&&', $this->filterString) as $andTags) {
121129
$satisfiesComma = false;
122130

123131
foreach (explode(',', $andTags) as $tag) {
124-
$tag = str_replace('@', '', trim($tag));
125-
126132
if ($tag[0] === '~') {
127133
$tag = mb_substr($tag, 1, mb_strlen($tag, 'utf8') - 1, 'utf8');
128134
$satisfiesComma = !in_array($tag, $tags, true) || $satisfiesComma;

src/GherkinCompatibilityMode.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public function allowWhitespaceInLanguageTag(): bool
103103
*/
104104
public function shouldRemoveTagPrefixChar(): bool
105105
{
106+
// Note: When this is removed we can also remove the code in TagFilter that handles tags with no leading @
106107
return match ($this) {
107108
self::LEGACY => true,
108109
default => false,

tests/Filter/TagFilterTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Behat\Gherkin\Node\OutlineNode;
1717
use Behat\Gherkin\Node\ScenarioNode;
1818
use ErrorException;
19+
use PHPUnit\Framework\Attributes\DataProvider;
1920
use PHPUnit\Framework\TestCase;
2021

2122
class TagFilterTest extends TestCase
@@ -300,6 +301,40 @@ public function testTagFilterThatIsAllWhitespaceIsIgnored(): void
300301
$this->assertTrue($result);
301302
}
302303

304+
/**
305+
* @phpstan-return list<array{string, list<string>, bool}>
306+
*/
307+
public static function providerMatchWithoutRemovingPrefix(): array
308+
{
309+
// These cases rely on the bulk of the coverage being provided by the other tests, and the knowledge that the
310+
// implementation ultimately uses the same logic to compare tags from all types of nodes. They are only intended
311+
// to be temporary until we drop legacy parsing mode, at which point we can add the `@` to all the tags in the
312+
// other tests in this file.
313+
return [
314+
['@wip', [], false],
315+
['@wip', ['@slow'], false],
316+
['@wip', ['@wip'], true],
317+
['@wip', ['@slow', '@wip'], true],
318+
['@tag1&&~@tag2&&@tag3', [], false],
319+
['@tag1&&~@tag2&&@tag3', ['@tag1'], false],
320+
['@tag1&&~@tag2&&@tag3', ['@tag1', '@tag3'], true],
321+
['@tag1&&~@tag2&&@tag3', ['@tag1', '@tag2'], false],
322+
['@tag1&&~@tag2&&@tag3', ['@tag1', '@tag4'], false],
323+
['@tag1&&~@tag2&&@tag3', ['@tag1', '@tag2', '@tag3'], false],
324+
];
325+
}
326+
327+
/**
328+
* @phpstan-param list<string> $tags
329+
*/
330+
#[DataProvider('providerMatchWithoutRemovingPrefix')]
331+
public function testItMatchesTagsParsedWithoutRemovingPrefix(string $filter, array $tags, bool $expect): void
332+
{
333+
$feature = new FeatureNode(null, null, $tags, null, [], '', '', null, 1);
334+
$tagFilter = new TagFilter($filter);
335+
$this->assertSame($expect, $tagFilter->isFeatureMatch($feature));
336+
}
337+
303338
private function expectDeprecationError(): void
304339
{
305340
set_error_handler(

0 commit comments

Comments
 (0)