feat: rewrites --dirty support

This commit is contained in:
Nuno Maduro
2023-01-10 22:23:52 +00:00
parent f6676118ac
commit 15931e2418
6 changed files with 102 additions and 84 deletions

View File

@ -3,10 +3,9 @@
use Pest\Actions\ValidatesEnvironment;
use Pest\ConfigLoader;
use Pest\Repositories\TestRepository;
use Pest\Support\Container;
use Pest\Kernel;
use Pest\Support\DirtyTestCaseFilter;
use Pest\Support\Container;
use Pest\TestCaseFilters\GitDirtyTestCaseFilter;
use Pest\TestSuite;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
@ -46,20 +45,17 @@ use Symfony\Component\Console\Output\OutputInterface;
$rootPath = dirname($autoloadPath, 2);
$argv = new ArgvInput();
$filters = [];
foreach ($args as $key => $value) {
if (str_contains($value, '--dirty')) {
$filters[] = new DirtyTestCaseFilter($rootPath);
}
}
$testSuite = TestSuite::getInstance(
$rootPath,
$argv->getParameterOption('--test-directory', (new ConfigLoader($rootPath))->getTestsDirectory()),
new TestRepository($filters),
);
foreach ($args as $key => $value) {
if (str_contains($value, '--dirty')) {
$testSuite->tests->filter(new GitDirtyTestCaseFilter($rootPath));
}
}
$isDecorated = $argv->getParameterOption('--colors', 'always') !== 'never';
$output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $isDecorated);

View File

@ -6,5 +6,8 @@ namespace Pest\Contracts;
interface TestCaseFilter
{
public function canLoad(string $suiteClassFile): bool;
/**
* Whether the test case is accepted.
*/
public function accept(string $testCaseFilename): bool;
}

View File

@ -29,11 +29,9 @@ final class TestRepository
private array $uses = [];
/**
* @param array<int, TestCaseFilter> $testCaseFilters
* @var array<int, TestCaseFilter>
*/
public function __construct(private readonly array $testCaseFilters = [])
{
}
private array $filters = [];
/**
* Counts the number of test cases.
@ -92,6 +90,14 @@ final class TestRepository
}
}
/**
* Filters the test cases using the given filter.
*/
public function filter(TestCaseFilter $filter): void
{
$this->filters[] = $filter;
}
/**
* Gets the test case factory from the given filename.
*/
@ -121,13 +127,13 @@ final class TestRepository
return;
}
$canLoad = array_reduce(
$this->testCaseFilters,
fn(bool $carry, TestCaseFilter $filter): bool => $carry && $filter->canLoad($filename),
$accepted = array_reduce(
$this->filters,
fn (bool $carry, TestCaseFilter $filter): bool => $carry && $filter->accept($filename),
true,
);
if ($canLoad) {
if ($accepted) {
$this->make($this->testCases[$filename]);
}
}

View File

@ -1,47 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Support;
use Pest\Contracts\TestCaseFilter;
use Symfony\Component\Process\Process;
final class DirtyTestCaseFilter implements TestCaseFilter
{
/**
* @var array<string>
*/
private array $changedFiles = [];
public function __construct(private string $projectRoot)
{
$this->loadDiff();
}
public function canLoad(string $suiteClassFile): bool
{
$relativePath = str_replace($this->projectRoot, '', $suiteClassFile);
if (str_starts_with($relativePath, '/')) {
$relativePath = substr($relativePath, 1);
}
return in_array($relativePath, $this->changedFiles, true);
}
private function loadDiff(): void
{
$process = new Process([
'git',
'diff',
'--name-only',
'HEAD',
'--',
'*.php',
]);
$process->run();
$this->changedFiles = explode(PHP_EOL, trim($process->getOutput()));
}
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Pest\TestCaseFilters;
use Pest\Contracts\TestCaseFilter;
use Pest\Exceptions\MissingDependency;
use Symfony\Component\Process\Process;
final class GitDirtyTestCaseFilter implements TestCaseFilter
{
/**
* @var array<string>
*/
private array $changedFiles = [];
public function __construct(private readonly string $projectRoot)
{
$this->loadDiff();
}
public function accept(string $testCaseFilename): bool
{
$relativePath = str_replace($this->projectRoot, '', $testCaseFilename);
if (str_starts_with($relativePath, '/')) {
$relativePath = substr($relativePath, 1);
}
return in_array($relativePath, $this->changedFiles, true);
}
private function loadDiff(): void
{
$process = new Process(['git', 'status', '--short', '--', '*.php']);
$process->run();
if (! $process->isSuccessful()) {
throw new MissingDependency('Filter by dirty files', 'git');
}
$output = preg_split('/\R+/', $process->getOutput(), flags: PREG_SPLIT_NO_EMPTY);
assert(is_array($output));
$dirtyFiles = [];
foreach ($output as $dirtyFile) {
$dirtyFiles[substr($dirtyFile, 3)] = trim(substr($dirtyFile, 0, 3));
}
$dirtyFiles = array_filter($dirtyFiles, fn ($status): bool => $status !== 'D');
$dirtyFiles = array_map(fn ($file, $status): string => in_array($status, ['R', 'RM'], true) ? explode(' -> ', $file)[1] : $file, array_keys($dirtyFiles), $dirtyFiles);
$this->changedFiles = $dirtyFiles = array_values($dirtyFiles);
}
}

View File

@ -23,6 +23,11 @@ final class TestSuite
*/
public ?TestCase $test = null;
/**
* Holds the tests repository.
*/
public TestRepository $tests;
/**
* Holds the before each repository.
*/
@ -64,11 +69,10 @@ final class TestSuite
public function __construct(
string $rootPath,
public string $testPath,
public TestRepository $tests = new TestRepository(),
)
{
) {
$this->beforeAll = new BeforeAllRepository();
$this->beforeEach = new BeforeEachRepository();
$this->tests = new TestRepository();
$this->afterEach = new AfterEachRepository();
$this->afterAll = new AfterAllRepository();
$this->retryRepository = new RetryRepository('retry');
@ -82,11 +86,9 @@ final class TestSuite
public static function getInstance(
string $rootPath = null,
string $testPath = null,
TestRepository $tests = null,
): TestSuite
{
): TestSuite {
if (is_string($rootPath) && is_string($testPath)) {
self::$instance = new TestSuite($rootPath, $testPath, $tests);
self::$instance = new TestSuite($rootPath, $testPath);
foreach (Plugin::$callables as $callable) {
$callable();