mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 07:47:22 +01:00
feat: custom presets
This commit is contained in:
@ -24,7 +24,7 @@
|
||||
"pestphp/pest-plugin": "^3.0.0",
|
||||
"pestphp/pest-plugin-arch": "^3.0.0",
|
||||
"pestphp/pest-plugin-mutate": "^3.0.0",
|
||||
"phpunit/phpunit": "^11.3.2"
|
||||
"phpunit/phpunit": "^11.3.3"
|
||||
},
|
||||
"conflict": {
|
||||
"sebastian/exporter": "<6.0.0",
|
||||
|
||||
@ -15,7 +15,7 @@ abstract class AbstractPreset // @pest-arch-ignore-line
|
||||
/**
|
||||
* The expectations.
|
||||
*
|
||||
* @var array<int, ArchExpectation>
|
||||
* @var array<int, Expectation<mixed>|ArchExpectation>
|
||||
*/
|
||||
protected array $expectations = [];
|
||||
|
||||
@ -24,7 +24,7 @@ abstract class AbstractPreset // @pest-arch-ignore-line
|
||||
*
|
||||
* @param array<int, string> $userNamespaces
|
||||
*/
|
||||
final public function __construct(
|
||||
public function __construct(
|
||||
private readonly array $userNamespaces,
|
||||
) {
|
||||
//
|
||||
@ -45,7 +45,7 @@ abstract class AbstractPreset // @pest-arch-ignore-line
|
||||
final public function ignoring(array|string $targetsOrDependencies): void
|
||||
{
|
||||
$this->expectations = array_map(
|
||||
fn (ArchExpectation $expectation): \Pest\Arch\Contracts\ArchExpectation => $expectation->ignoring($targetsOrDependencies),
|
||||
fn (ArchExpectation|Expectation $expectation): Expectation|ArchExpectation => $expectation instanceof ArchExpectation ? $expectation->ignoring($targetsOrDependencies) : $expectation,
|
||||
$this->expectations,
|
||||
);
|
||||
}
|
||||
|
||||
45
src/ArchPresets/Custom.php
Normal file
45
src/ArchPresets/Custom.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\ArchPresets;
|
||||
|
||||
use Closure;
|
||||
use Pest\Arch\Contracts\ArchExpectation;
|
||||
use Pest\Expectation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Custom extends AbstractPreset
|
||||
{
|
||||
/**
|
||||
* Creates a new preset instance.
|
||||
*
|
||||
* @param array<int, string> $userNamespaces
|
||||
* @param Closure(array<int, string>): array<Expectation<mixed>|ArchExpectation> $execute
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $userNamespaces,
|
||||
private readonly string $name,
|
||||
private readonly Closure $execute,
|
||||
) {
|
||||
parent::__construct($userNamespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the preset.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the arch preset.
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
$this->expectations = ($this->execute)($this->userNamespaces);
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ namespace Pest\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Pest\Exceptions\DatasetArgumentsMismatch;
|
||||
use Pest\Preset;
|
||||
use Pest\Support\ChainableClosure;
|
||||
use Pest\Support\ExceptionTrace;
|
||||
use Pest\Support\Reflection;
|
||||
@ -410,6 +411,14 @@ trait Testable
|
||||
return ExceptionTrace::ensure(fn (): mixed => call_user_func_array(Closure::bind($closure, $this, $this::class), $arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the given preset on the test.
|
||||
*/
|
||||
public function preset(): Preset
|
||||
{
|
||||
return new Preset;
|
||||
}
|
||||
|
||||
#[PostCondition]
|
||||
protected function __MarkTestIncompleteIfSnapshotHaveChanged(): void
|
||||
{
|
||||
|
||||
@ -86,6 +86,14 @@ final readonly class Configuration
|
||||
return new Configuration\Theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the presets configuration.
|
||||
*/
|
||||
public function presets(): Configuration\Presets
|
||||
{
|
||||
return new Configuration\Presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the project configuration.
|
||||
*/
|
||||
|
||||
19
src/Configuration/Presets.php
Normal file
19
src/Configuration/Presets.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Configuration;
|
||||
|
||||
use Closure;
|
||||
use Pest\Preset;
|
||||
|
||||
final class Presets
|
||||
{
|
||||
/**
|
||||
* Creates a custom preset instance, and adds it to the list of presets.
|
||||
*/
|
||||
public function custom(string $name, Closure $execute): void
|
||||
{
|
||||
Preset::custom($name, $execute);
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,6 @@ use Pest\Factories\TestCaseMethodFactory;
|
||||
use Pest\Mutate\Decorators\TestCallDecorator as MutationTestCallDecorator;
|
||||
use Pest\PendingCalls\Concerns\Describable;
|
||||
use Pest\Plugins\Only;
|
||||
use Pest\Preset;
|
||||
use Pest\Support\Backtrace;
|
||||
use Pest\Support\Exporter;
|
||||
use Pest\Support\HigherOrderCallables;
|
||||
@ -663,12 +662,4 @@ final class TestCall
|
||||
$testCase->attributes = array_merge($testCase->attributes, $this->testCaseFactoryAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the given preset on the test.
|
||||
*/
|
||||
public function preset(): Preset
|
||||
{
|
||||
return new Preset($this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,13 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
|
||||
use Closure;
|
||||
use Pest\Arch\Support\Composer;
|
||||
use Pest\ArchPresets\AbstractPreset;
|
||||
use Pest\ArchPresets\Custom;
|
||||
use Pest\ArchPresets\Laravel;
|
||||
use Pest\ArchPresets\Php;
|
||||
use Pest\ArchPresets\Relaxed;
|
||||
use Pest\ArchPresets\Security;
|
||||
use Pest\ArchPresets\Strict;
|
||||
use Pest\Exceptions\InvalidArgumentException;
|
||||
use Pest\PendingCalls\TestCall;
|
||||
use stdClass;
|
||||
|
||||
@ -26,10 +29,17 @@ final class Preset
|
||||
*/
|
||||
private static ?array $baseNamespaces = null;
|
||||
|
||||
/**
|
||||
* The custom presets.
|
||||
*
|
||||
* @var array<string, Closure>
|
||||
*/
|
||||
private static array $customPresets = [];
|
||||
|
||||
/**
|
||||
* Creates a new preset instance.
|
||||
*/
|
||||
public function __construct(private readonly TestCall $testCall)
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
@ -74,6 +84,41 @@ final class Preset
|
||||
return $this->executePreset(new Relaxed($this->baseNamespaces()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the Pest custom preset and returns the test call instance.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function custom(string $name, Closure $execute): void
|
||||
{
|
||||
if (preg_match('/^[a-zA-Z]+$/', $name) === false) {
|
||||
throw new InvalidArgumentException('The preset name must only contain words from a-z or A-Z.');
|
||||
}
|
||||
|
||||
self::$customPresets[$name] = $execute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically handle calls to the class.
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __call(string $name, array $arguments): AbstractPreset
|
||||
{
|
||||
if (! array_key_exists($name, self::$customPresets)) {
|
||||
$availablePresets = [
|
||||
...['php', 'laravel', 'strict', 'security', 'relaxed'],
|
||||
...array_keys(self::$customPresets),
|
||||
];
|
||||
|
||||
throw new InvalidArgumentException(sprintf('The preset [%s] does not exist. The available presets are [%s].', $name, implode(', ', $availablePresets)));
|
||||
}
|
||||
|
||||
return $this->executePreset(new Custom($this->baseNamespaces(), $name, self::$customPresets[$name]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given preset.
|
||||
*
|
||||
@ -84,19 +129,13 @@ final class Preset
|
||||
*/
|
||||
private function executePreset(AbstractPreset $preset): AbstractPreset
|
||||
{
|
||||
if ((fn (): ?string => $this->description)->call($this->testCall) === null) {
|
||||
$description = strtolower((new \ReflectionClass($preset))->getShortName());
|
||||
|
||||
(fn (): string => $this->description = sprintf('arch "%s" preset', $description))->call($this->testCall);
|
||||
}
|
||||
|
||||
$this->baseNamespaces();
|
||||
|
||||
$preset->execute();
|
||||
|
||||
$this->testCall->testCaseMethod->closure = (function () use ($preset): void {
|
||||
$preset->flush();
|
||||
})->bindTo(new stdClass);
|
||||
// $this->testCall->testCaseMethod->closure = (function () use ($preset): void {
|
||||
// $preset->flush();
|
||||
// })->bindTo(new stdClass);
|
||||
|
||||
return $preset;
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@
|
||||
--fail-on-warning Signal failure using shell exit code when a warning was triggered
|
||||
--fail-on-risky Signal failure using shell exit code when a test was considered risky
|
||||
--fail-on-deprecation Signal failure using shell exit code when a deprecation was triggered
|
||||
--fail-on-phpunit-deprecation Signal failure using shell exit code when a PHPUnit deprecation was triggered
|
||||
--fail-on-notice Signal failure using shell exit code when a notice was triggered
|
||||
--fail-on-skipped Signal failure using shell exit code when a test was skipped
|
||||
--fail-on-incomplete Signal failure using shell exit code when a test was marked incomplete
|
||||
@ -85,6 +86,7 @@
|
||||
--display-incomplete .................. Display details for incomplete tests
|
||||
--display-skipped ........................ Display details for skipped tests
|
||||
--display-deprecations . Display details for deprecations triggered by tests
|
||||
--display-phpunit-deprecations .... Display details for PHPUnit deprecations
|
||||
--display-errors ............. Display details for errors triggered by tests
|
||||
--display-notices ........... Display details for notices triggered by tests
|
||||
--display-warnings ......... Display details for warnings triggered by tests
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
|
||||
PASS Tests\Arch
|
||||
✓ arch "php" preset
|
||||
✓ arch "strict" preset
|
||||
✓ arch "security" preset
|
||||
✓ preset → php → ignoring ['Pest\Expectation', 'debug_backtrace', 'var_export', …]
|
||||
✓ preset → strict → ignoring ['usleep']
|
||||
✓ preset → security → ignoring ['eval', 'str_shuffle', 'exec', …]
|
||||
✓ globals
|
||||
✓ dependencies
|
||||
✓ contracts
|
||||
@ -1423,6 +1423,10 @@
|
||||
PASS Tests\Unit\Plugins\Retry
|
||||
✓ it orders by defects and stop on defects if when --retry is used
|
||||
|
||||
PASS Tests\Unit\Preset
|
||||
✓ preset invalid name
|
||||
✓ preset → myFramework
|
||||
|
||||
PASS Tests\Unit\Support\Backtrace
|
||||
✓ it gets file name from called file
|
||||
|
||||
@ -1574,4 +1578,4 @@
|
||||
WARN Tests\Visual\Version
|
||||
- visual snapshot of help command output
|
||||
|
||||
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 17 todos, 28 skipped, 1089 passed (2621 assertions)
|
||||
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 17 todos, 28 skipped, 1091 passed (2624 assertions)
|
||||
13
tests/Unit/Preset.php
Normal file
13
tests/Unit/Preset.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
pest()->presets()->custom('myFramework', function (array $userNamespaces) {
|
||||
return [
|
||||
expect($userNamespaces)->toBe(['Pest']),
|
||||
];
|
||||
});
|
||||
|
||||
test('preset invalid name', function () {
|
||||
$this->preset()->myAnotherFramework();
|
||||
})->throws(InvalidArgumentException::class, 'The preset [myAnotherFramework] does not exist. The available presets are [php, laravel, strict, security, relaxed, myFramework].');
|
||||
|
||||
arch()->preset()->myFramework();
|
||||
@ -16,7 +16,7 @@ $run = function () {
|
||||
|
||||
test('parallel', function () use ($run) {
|
||||
expect($run('--exclude-group=integration'))
|
||||
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 17 todos, 19 skipped, 1079 passed (2597 assertions)')
|
||||
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 17 todos, 19 skipped, 1081 passed (2600 assertions)')
|
||||
->toContain('Parallel: 3 processes');
|
||||
})->skipOnWindows();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user