Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

### Fixed
- **Unanchored built-in directory exclude patterns dropped legitimate directories**: `ext/`, `examples?/`, `tests?/`, `php4/`, `dev-bin/`, `.github/`, `.gitlab/` behaved as substring matches, so e.g. `Text/` and `Context/` were treated as `ext/` and silently omitted from the scoped output. The seven patterns now anchor to path-start or immediately after `/`.

---

## 1.2.5 - 2026-04-07

### Fixed
Expand Down
14 changes: 7 additions & 7 deletions src/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ class Config
'/\\.phpunit/i',
'/\\.editorconfig$/',
'/\\.gitignore$/',
'/\\.github\\//i',
'/\\.gitlab\\//i',
'/examples?\\//i',
'/ext\\//i',
'/php4\\//i',
'/tests?\\//i',
'/(?:^|\\/)\\.github\\//i',
'/(?:^|\\/)\\.gitlab\\//i',
'/(?:^|\\/)examples?\\//i',
'/(?:^|\\/)ext\\//i',
'/(?:^|\\/)php4\\//i',
'/(?:^|\\/)tests?\\//i',
'/\\bbin\\//i',
'/dev-bin\\//i',
'/(?:^|\\/)dev-bin\\//i',
'/Makefile$/',
'/phpunit\\.xml(\\.dist)?$/i',
'/\\.travis\\.yml$/',
Expand Down
95 changes: 95 additions & 0 deletions tests/Unit/Config/BuiltInExcludePatternsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace VeronaLabs\WpScoper\Tests\Unit\Config;

use PHPUnit\Framework\TestCase;
use VeronaLabs\WpScoper\Config\Config;

class BuiltInExcludePatternsTest extends TestCase
{
private const P_GITHUB = '/(?:^|\\/)\\.github\\//i';
private const P_GITLAB = '/(?:^|\\/)\\.gitlab\\//i';
private const P_EXAMPLES = '/(?:^|\\/)examples?\\//i';
private const P_EXT = '/(?:^|\\/)ext\\//i';
private const P_PHP4 = '/(?:^|\\/)php4\\//i';
private const P_TESTS = '/(?:^|\\/)tests?\\//i';
private const P_DEV_BIN = '/(?:^|\\/)dev-bin\\//i';

public function testAnchoredPatternsAreDeclaredAsBuiltIn(): void
{
$patterns = Config::fromArray([
'namespace_prefix' => 'Test\\Deps',
'packages' => [],
], '/tmp')->getExcludePatterns();

foreach (
[
self::P_GITHUB,
self::P_GITLAB,
self::P_EXAMPLES,
self::P_EXT,
self::P_PHP4,
self::P_TESTS,
self::P_DEV_BIN,
] as $pattern
) {
$this->assertContains($pattern, $patterns);
}
}

/**
* @dataProvider directoryPatternMatchProvider
*/
public function testBuiltInDirectoryPatternMatchesExpectedPaths(
string $pattern,
string $relativePath,
bool $shouldMatch
): void {
$this->assertSame($shouldMatch, preg_match($pattern, $relativePath) === 1);
}

/**
* @return array<string, array{0: string, 1: string, 2: bool}>
*/
public static function directoryPatternMatchProvider(): array
{
return [
'.github at root matches' => [self::P_GITHUB, '.github/workflows/ci.yml', true],
'.github nested matches' => [self::P_GITHUB, 'src/.github/foo.php', true],
'notgithub/ does not match' => [self::P_GITHUB, 'notgithub/foo.php', false],

'.gitlab at root matches' => [self::P_GITLAB, '.gitlab/ci.yml', true],
'.gitlab nested matches' => [self::P_GITLAB, 'src/.gitlab/foo.php', true],
'notgitlab/ does not match' => [self::P_GITLAB, 'notgitlab/foo.php', false],

'example/ at root matches' => [self::P_EXAMPLES, 'example/foo.php', true],
'examples/ at root matches' => [self::P_EXAMPLES, 'examples/foo.php', true],
'examples/ nested matches' => [self::P_EXAMPLES, 'src/examples/foo.php', true],
'MyExamples/ does not match' => [self::P_EXAMPLES, 'MyExamples/foo.php', false],
'bad-examples/ does not match' => [self::P_EXAMPLES, 'bad-examples/foo.php', false],
'nested MyExamples/ does not match' => [self::P_EXAMPLES, 'src/MyExamples/foo.php', false],

'ext/ at root matches' => [self::P_EXT, 'ext/Foo.php', true],
'ext/ nested matches' => [self::P_EXT, 'src/ext/Foo.php', true],
'Text/ does not match' => [self::P_EXT, 'Text/Foo.php', false],
'Context/ does not match' => [self::P_EXT, 'Context/Foo.php', false],
'nested Text/ does not match' => [self::P_EXT, 'src/Text/Foo.php', false],

'php4/ at root matches' => [self::P_PHP4, 'php4/foo.php', true],
'php4/ nested matches' => [self::P_PHP4, 'src/php4/foo.php', true],
'graphql4/ does not match' => [self::P_PHP4, 'graphql4/foo.php', false],

'test/ at root matches' => [self::P_TESTS, 'test/foo.php', true],
'tests/ at root matches' => [self::P_TESTS, 'tests/foo.php', true],
'tests/ nested matches' => [self::P_TESTS, 'src/tests/foo.php', true],
'UnitTests/ does not match' => [self::P_TESTS, 'UnitTests/foo.php', false],
'apitest/ does not match' => [self::P_TESTS, 'apitest/foo.php', false],

'dev-bin/ at root matches' => [self::P_DEV_BIN, 'dev-bin/foo.php', true],
'dev-bin/ nested matches' => [self::P_DEV_BIN, 'src/dev-bin/foo.php', true],
'my-dev-bin/ does not match' => [self::P_DEV_BIN, 'my-dev-bin/foo.php', false],
];
}
}
4 changes: 2 additions & 2 deletions tests/Unit/Config/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ public function testDefaultValues(): void
$this->assertSame([], $config->getExcludePackages());
// Built-in patterns are always included
$this->assertContains('/\\.md$/i', $config->getExcludePatterns());
$this->assertContains('/examples?\\//i', $config->getExcludePatterns());
$this->assertContains('/ext\\//i', $config->getExcludePatterns());
$this->assertContains('/(?:^|\\/)examples?\\//i', $config->getExcludePatterns());
$this->assertContains('/(?:^|\\/)ext\\//i', $config->getExcludePatterns());
$this->assertSame(['views', 'templates', 'resources'], $config->getExcludeDirectories());
$this->assertFalse($config->shouldDeleteVendorPackages());
$this->assertTrue($config->shouldUpdateCallSites());
Expand Down
Loading