mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 07:47:22 +01:00
Adds initial implementation of --dirty option.
This commit is contained in:
25
bin/pest
25
bin/pest
@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
use Pest\Actions\ValidatesEnvironment;
|
use Pest\Actions\ValidatesEnvironment;
|
||||||
use Pest\ConfigLoader;
|
use Pest\ConfigLoader;
|
||||||
|
use Pest\Repositories\TestRepository;
|
||||||
use Pest\Support\Container;
|
use Pest\Support\Container;
|
||||||
use Pest\Kernel;
|
use Pest\Kernel;
|
||||||
|
use Pest\Support\DirtyTestCaseFilter;
|
||||||
use Pest\TestSuite;
|
use Pest\TestSuite;
|
||||||
use Symfony\Component\Console\Input\ArgvInput;
|
use Symfony\Component\Console\Input\ArgvInput;
|
||||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||||
@ -44,9 +46,18 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
$rootPath = dirname($autoloadPath, 2);
|
$rootPath = dirname($autoloadPath, 2);
|
||||||
$argv = new ArgvInput();
|
$argv = new ArgvInput();
|
||||||
|
|
||||||
|
$filters = [];
|
||||||
|
|
||||||
|
foreach ($args as $key => $value) {
|
||||||
|
if (str_contains($value, '--dirty')) {
|
||||||
|
$filters[] = new DirtyTestCaseFilter($rootPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$testSuite = TestSuite::getInstance(
|
$testSuite = TestSuite::getInstance(
|
||||||
$rootPath,
|
$rootPath,
|
||||||
$argv->getParameterOption('--test-directory', (new ConfigLoader($rootPath))->getTestsDirectory())
|
$argv->getParameterOption('--test-directory', (new ConfigLoader($rootPath))->getTestsDirectory()),
|
||||||
|
new TestRepository($filters),
|
||||||
);
|
);
|
||||||
|
|
||||||
$isDecorated = $argv->getParameterOption('--colors', 'always') !== 'never';
|
$isDecorated = $argv->getParameterOption('--colors', 'always') !== 'never';
|
||||||
@ -57,16 +68,10 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
$container->add(TestSuite::class, $testSuite);
|
$container->add(TestSuite::class, $testSuite);
|
||||||
$container->add(OutputInterface::class, $output);
|
$container->add(OutputInterface::class, $output);
|
||||||
|
|
||||||
|
$argsToUnset = ['--test-directory', '--compact', '--profile', '--dirty'];
|
||||||
|
|
||||||
foreach ($args as $key => $value) {
|
foreach ($args as $key => $value) {
|
||||||
if (str_contains($value, '--test-directory')) {
|
if (in_array($value, $argsToUnset)) {
|
||||||
unset($args[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str_contains($value, '--compact')) {
|
|
||||||
unset($args[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str_contains($value, '--profile')) {
|
|
||||||
unset($args[$key]);
|
unset($args[$key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/Contracts/TestCaseFilter.php
Normal file
10
src/Contracts/TestCaseFilter.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Pest\Contracts;
|
||||||
|
|
||||||
|
interface TestCaseFilter
|
||||||
|
{
|
||||||
|
public function canLoad(string $suiteClassFile): bool;
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Pest\Repositories;
|
namespace Pest\Repositories;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Pest\Contracts\TestCaseFilter;
|
||||||
use Pest\Exceptions\TestCaseAlreadyInUse;
|
use Pest\Exceptions\TestCaseAlreadyInUse;
|
||||||
use Pest\Exceptions\TestCaseClassOrTraitNotFound;
|
use Pest\Exceptions\TestCaseClassOrTraitNotFound;
|
||||||
use Pest\Factories\TestCaseFactory;
|
use Pest\Factories\TestCaseFactory;
|
||||||
@ -27,6 +28,13 @@ final class TestRepository
|
|||||||
*/
|
*/
|
||||||
private array $uses = [];
|
private array $uses = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, TestCaseFilter> $testCaseFilters
|
||||||
|
*/
|
||||||
|
public function __construct(private readonly array $testCaseFilters = [])
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts the number of test cases.
|
* Counts the number of test cases.
|
||||||
*/
|
*/
|
||||||
@ -42,22 +50,22 @@ final class TestRepository
|
|||||||
*/
|
*/
|
||||||
public function getFilenames(): array
|
public function getFilenames(): array
|
||||||
{
|
{
|
||||||
$testCases = array_filter($this->testCases, static fn (TestCaseFactory $testCase) => $testCase->methodsUsingOnly() !== []);
|
$testCases = array_filter($this->testCases, static fn(TestCaseFactory $testCase) => $testCase->methodsUsingOnly() !== []);
|
||||||
|
|
||||||
if ($testCases === []) {
|
if ($testCases === []) {
|
||||||
$testCases = $this->testCases;
|
$testCases = $this->testCases;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_values(array_map(static fn (TestCaseFactory $factory): string => $factory->filename, $testCases));
|
return array_values(array_map(static fn(TestCaseFactory $factory): string => $factory->filename, $testCases));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the given `$testCaseClass` on the given `$paths`.
|
* Uses the given `$testCaseClass` on the given `$paths`.
|
||||||
*
|
*
|
||||||
* @param array<int, string> $classOrTraits
|
* @param array<int, string> $classOrTraits
|
||||||
* @param array<int, string> $groups
|
* @param array<int, string> $groups
|
||||||
* @param array<int, string> $paths
|
* @param array<int, string> $paths
|
||||||
* @param array<int, Closure> $hooks
|
* @param array<int, Closure> $hooks
|
||||||
*/
|
*/
|
||||||
public function use(array $classOrTraits, array $groups, array $paths, array $hooks): void
|
public function use(array $classOrTraits, array $groups, array $paths, array $hooks): void
|
||||||
{
|
{
|
||||||
@ -97,7 +105,7 @@ final class TestRepository
|
|||||||
*/
|
*/
|
||||||
public function set(TestCaseMethodFactory $method): void
|
public function set(TestCaseMethodFactory $method): void
|
||||||
{
|
{
|
||||||
if (! array_key_exists($method->filename, $this->testCases)) {
|
if (!array_key_exists($method->filename, $this->testCases)) {
|
||||||
$this->testCases[$method->filename] = new TestCaseFactory($method->filename);
|
$this->testCases[$method->filename] = new TestCaseFactory($method->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +117,17 @@ final class TestRepository
|
|||||||
*/
|
*/
|
||||||
public function makeIfNeeded(string $filename): void
|
public function makeIfNeeded(string $filename): void
|
||||||
{
|
{
|
||||||
if (array_key_exists($filename, $this->testCases)) {
|
if (!array_key_exists($filename, $this->testCases)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$canLoad = array_reduce(
|
||||||
|
$this->testCaseFilters,
|
||||||
|
fn(bool $carry, TestCaseFilter $filter): bool => $carry && $filter->canLoad($filename),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($canLoad) {
|
||||||
$this->make($this->testCases[$filename]);
|
$this->make($this->testCases[$filename]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,12 +137,12 @@ final class TestRepository
|
|||||||
*/
|
*/
|
||||||
private function make(TestCaseFactory $testCase): void
|
private function make(TestCaseFactory $testCase): void
|
||||||
{
|
{
|
||||||
$startsWith = static fn (string $target, string $directory): bool => Str::startsWith($target, $directory.DIRECTORY_SEPARATOR);
|
$startsWith = static fn(string $target, string $directory): bool => Str::startsWith($target, $directory . DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
foreach ($this->uses as $path => $uses) {
|
foreach ($this->uses as $path => $uses) {
|
||||||
[$classOrTraits, $groups, $hooks] = $uses;
|
[$classOrTraits, $groups, $hooks] = $uses;
|
||||||
|
|
||||||
if ((! is_dir($path) && $testCase->filename === $path) || (is_dir($path) && $startsWith($testCase->filename, $path))) {
|
if ((!is_dir($path) && $testCase->filename === $path) || (is_dir($path) && $startsWith($testCase->filename, $path))) {
|
||||||
foreach ($classOrTraits as $class) {
|
foreach ($classOrTraits as $class) {
|
||||||
/** @var string $class */
|
/** @var string $class */
|
||||||
if (class_exists($class)) {
|
if (class_exists($class)) {
|
||||||
|
|||||||
47
src/Support/DirtyTestCaseFilter.php
Normal file
47
src/Support/DirtyTestCaseFilter.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -23,11 +23,6 @@ final class TestSuite
|
|||||||
*/
|
*/
|
||||||
public ?TestCase $test = null;
|
public ?TestCase $test = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the tests repository.
|
|
||||||
*/
|
|
||||||
public TestRepository $tests;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the before each repository.
|
* Holds the before each repository.
|
||||||
*/
|
*/
|
||||||
@ -68,11 +63,12 @@ final class TestSuite
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $rootPath,
|
string $rootPath,
|
||||||
public string $testPath)
|
public string $testPath,
|
||||||
|
public TestRepository $tests = new TestRepository(),
|
||||||
|
)
|
||||||
{
|
{
|
||||||
$this->beforeAll = new BeforeAllRepository();
|
$this->beforeAll = new BeforeAllRepository();
|
||||||
$this->beforeEach = new BeforeEachRepository();
|
$this->beforeEach = new BeforeEachRepository();
|
||||||
$this->tests = new TestRepository();
|
|
||||||
$this->afterEach = new AfterEachRepository();
|
$this->afterEach = new AfterEachRepository();
|
||||||
$this->afterAll = new AfterAllRepository();
|
$this->afterAll = new AfterAllRepository();
|
||||||
$this->retryTempRepository = new TempRepository('retry');
|
$this->retryTempRepository = new TempRepository('retry');
|
||||||
@ -83,10 +79,14 @@ final class TestSuite
|
|||||||
/**
|
/**
|
||||||
* Returns the current instance of the test suite.
|
* Returns the current instance of the test suite.
|
||||||
*/
|
*/
|
||||||
public static function getInstance(string $rootPath = null, string $testPath = null): TestSuite
|
public static function getInstance(
|
||||||
|
string $rootPath = null,
|
||||||
|
string $testPath = null,
|
||||||
|
TestRepository $tests = null,
|
||||||
|
): TestSuite
|
||||||
{
|
{
|
||||||
if (is_string($rootPath) && is_string($testPath)) {
|
if (is_string($rootPath) && is_string($testPath)) {
|
||||||
self::$instance = new TestSuite($rootPath, $testPath);
|
self::$instance = new TestSuite($rootPath, $testPath, $tests);
|
||||||
|
|
||||||
foreach (Plugin::$callables as $callable) {
|
foreach (Plugin::$callables as $callable) {
|
||||||
$callable();
|
$callable();
|
||||||
|
|||||||
Reference in New Issue
Block a user