mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 07:47:22 +01:00
feat(describe): continues work around hooks
This commit is contained in:
@ -141,7 +141,7 @@ trait Testable
|
||||
}
|
||||
|
||||
$this->{$property} = ($this->{$property} instanceof Closure)
|
||||
? ChainableClosure::from($this->{$property}, $hook)
|
||||
? ChainableClosure::fromSameObject($this->{$property}, $hook)
|
||||
: $hook;
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ trait Testable
|
||||
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename)[1];
|
||||
|
||||
if ($this->__beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::from($this->__beforeEach, $beforeEach);
|
||||
$beforeEach = ChainableClosure::fromSameObject($this->__beforeEach, $beforeEach);
|
||||
}
|
||||
|
||||
$this->__callClosure($beforeEach, func_get_args());
|
||||
@ -203,7 +203,7 @@ trait Testable
|
||||
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
|
||||
|
||||
if ($this->__afterEach instanceof Closure) {
|
||||
$afterEach = ChainableClosure::from($this->__afterEach, $afterEach);
|
||||
$afterEach = ChainableClosure::fromSameObject($this->__afterEach, $afterEach);
|
||||
}
|
||||
|
||||
$this->__callClosure($afterEach, func_get_args());
|
||||
|
||||
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
use Pest\Exceptions\AfterAllWithinDescribe;
|
||||
use Pest\Exceptions\BeforeAllWithinDescribe;
|
||||
use Pest\Expectation;
|
||||
use Pest\PendingCalls;
|
||||
use Pest\PendingCalls\AfterEachCall;
|
||||
use Pest\PendingCalls\BeforeEachCall;
|
||||
use Pest\PendingCalls\DescribeCall;
|
||||
@ -39,7 +38,7 @@ if (! function_exists('beforeAll')) {
|
||||
*/
|
||||
function beforeAll(Closure $closure): void
|
||||
{
|
||||
if (! is_null(PendingCalls::$describing)) {
|
||||
if (! is_null(DescribeCall::describing())) {
|
||||
$filename = Backtrace::file();
|
||||
|
||||
throw new BeforeAllWithinDescribe($filename);
|
||||
@ -89,11 +88,7 @@ if (! function_exists('describe')) {
|
||||
{
|
||||
$filename = Backtrace::testFile();
|
||||
|
||||
PendingCalls::startDescribe(
|
||||
$describeCall = new DescribeCall(TestSuite::getInstance(), $filename, $description, $tests),
|
||||
);
|
||||
|
||||
return $describeCall;
|
||||
return new DescribeCall(TestSuite::getInstance(), $filename, $description, $tests);
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +184,7 @@ if (! function_exists('afterAll')) {
|
||||
*/
|
||||
function afterAll(Closure $closure): void
|
||||
{
|
||||
if (! is_null(PendingCalls::$describing)) {
|
||||
if (! is_null(DescribeCall::describing())) {
|
||||
$filename = Backtrace::file();
|
||||
|
||||
throw new AfterAllWithinDescribe($filename);
|
||||
|
||||
@ -4,11 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
|
||||
use Closure;
|
||||
use Pest\PendingCalls\AfterEachCall;
|
||||
use Pest\PendingCalls\BeforeEachCall;
|
||||
use Pest\PendingCalls\DescribeCall;
|
||||
use Pest\PendingCalls\TestCall;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -20,96 +16,11 @@ final class PendingCalls
|
||||
*/
|
||||
public static ?string $describing = null;
|
||||
|
||||
/**
|
||||
* The list of before each pending calls.
|
||||
*
|
||||
* @var array<int, BeforeEachCall>
|
||||
*/
|
||||
public static array $beforeEachCalls = [];
|
||||
|
||||
/**
|
||||
* The list of test pending calls.
|
||||
*
|
||||
* @var array<int, array{0: TestCall, 1: Closure}>
|
||||
*/
|
||||
public static array $testCalls = [];
|
||||
|
||||
/**
|
||||
* The list of after each pending calls.
|
||||
*
|
||||
* @var array<int, array{0: AfterEachCall, 1: Closure}>
|
||||
*/
|
||||
public static array $afterEachCalls = [];
|
||||
|
||||
/**
|
||||
* Sets the current describe call.
|
||||
*/
|
||||
public static function startDescribe(DescribeCall $describeCall): void
|
||||
public static function describe(DescribeCall $describeCall): void
|
||||
{
|
||||
self::$describing = $describeCall->description;
|
||||
|
||||
($describeCall->tests)();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new before each call.
|
||||
*/
|
||||
public static function beforeEach(BeforeEachCall $beforeEachCall, Closure $setter): void
|
||||
{
|
||||
$setter($beforeEachCall->describing = self::$describing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new test call.
|
||||
*/
|
||||
public static function test(TestCall $testCall, Closure $setter): void
|
||||
{
|
||||
if (! is_null($testCall->describing = self::$describing)) {
|
||||
self::$testCalls[] = [$testCall, $setter];
|
||||
} else {
|
||||
$setter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new before each call.
|
||||
*/
|
||||
public static function afterEach(AfterEachCall $afterEachCall, Closure $setter): void
|
||||
{
|
||||
if (! is_null(self::$describing)) {
|
||||
$afterEachCall->describing = self::$describing;
|
||||
|
||||
self::$afterEachCalls[] = [$afterEachCall, $setter];
|
||||
} else {
|
||||
$setter();
|
||||
}
|
||||
}
|
||||
|
||||
public static function endDescribe(DescribeCall $describeCall): void
|
||||
{
|
||||
$describing = self::$describing;
|
||||
|
||||
self::$describing = null;
|
||||
|
||||
foreach (self::$beforeEachCalls as [$beforeEachCall, $setter]) {
|
||||
$setter($describing);
|
||||
}
|
||||
|
||||
self::$beforeEachCalls = [];
|
||||
|
||||
foreach (self::$testCalls as [$testCall, $setter]) {
|
||||
/** @var TestCall $testCall */
|
||||
$testCall->testCaseMethod->description = '`'.$describeCall->description.'` '.$testCall->testCaseMethod->description;
|
||||
|
||||
$setter($describing);
|
||||
}
|
||||
|
||||
self::$testCalls = [];
|
||||
|
||||
foreach (self::$afterEachCalls as [$afterEachCall, $setter]) {
|
||||
$setter($describing);
|
||||
}
|
||||
|
||||
self::$afterEachCalls = [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Pest\PendingCalls;
|
||||
|
||||
use Closure;
|
||||
use Pest\PendingCalls;
|
||||
use Pest\PendingCalls\Concerns\Describable;
|
||||
use Pest\Support\Backtrace;
|
||||
use Pest\Support\ChainableClosure;
|
||||
@ -41,6 +40,8 @@ final class AfterEachCall
|
||||
$this->closure = $closure instanceof Closure ? $closure : NullClosure::create();
|
||||
|
||||
$this->proxies = new HigherOrderMessageCollection();
|
||||
|
||||
$this->describing = DescribeCall::describing();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,12 +49,13 @@ final class AfterEachCall
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
PendingCalls::afterEach($this, function (string $describing = null) {
|
||||
$describing = $this->describing;
|
||||
|
||||
$proxies = $this->proxies;
|
||||
|
||||
$afterEachTestCase = ChainableClosure::when(
|
||||
fn () => is_null($describing) || $this->__describeDescription === $describing, // @phpstan-ignore-line
|
||||
ChainableClosure::from(fn () => $proxies->chain($this), $this->closure)->bindTo($this, self::class), // @phpstan-ignore-line
|
||||
ChainableClosure::fromSameObject(fn () => $proxies->chain($this), $this->closure)->bindTo($this, self::class), // @phpstan-ignore-line
|
||||
)->bindTo($this, self::class);
|
||||
|
||||
assert($afterEachTestCase instanceof Closure);
|
||||
@ -63,7 +65,6 @@ final class AfterEachCall
|
||||
$this,
|
||||
$afterEachTestCase,
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Pest\PendingCalls;
|
||||
|
||||
use Closure;
|
||||
use Pest\PendingCalls;
|
||||
use Pest\PendingCalls\Concerns\Describable;
|
||||
use Pest\Support\Backtrace;
|
||||
use Pest\Support\ChainableClosure;
|
||||
@ -47,6 +46,8 @@ final class BeforeEachCall
|
||||
|
||||
$this->testCallProxies = new HigherOrderMessageCollection();
|
||||
$this->testCaseProxies = new HigherOrderMessageCollection();
|
||||
|
||||
$this->describing = DescribeCall::describing();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,18 +55,18 @@ final class BeforeEachCall
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
PendingCalls::beforeEach($this, function (string $describing = null) {
|
||||
$describing = $this->describing;
|
||||
$testCaseProxies = $this->testCaseProxies;
|
||||
|
||||
$beforeEachTestCall = function (TestCall $testCall) use ($describing): void {
|
||||
if (is_null($describing) || ($this->describing === $testCall->describing && $testCall->describing === $describing)) {
|
||||
if ($describing === $this->describing && $describing === $testCall->describing) {
|
||||
$this->testCallProxies->chain($testCall);
|
||||
}
|
||||
};
|
||||
|
||||
$beforeEachTestCase = ChainableClosure::when(
|
||||
fn () => is_null($describing) || $this->__describeDescription === $describing, // @phpstan-ignore-line
|
||||
ChainableClosure::from(fn () => $testCaseProxies->chain($this), $this->closure)->bindTo($this, self::class), // @phpstan-ignore-line
|
||||
ChainableClosure::fromSameObject(fn () => $testCaseProxies->chain($this), $this->closure)->bindTo($this, self::class), // @phpstan-ignore-line
|
||||
)->bindTo($this, self::class);
|
||||
|
||||
assert($beforeEachTestCase instanceof Closure);
|
||||
@ -76,7 +77,6 @@ final class BeforeEachCall
|
||||
$beforeEachTestCall,
|
||||
$beforeEachTestCase,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,7 +86,6 @@ final class BeforeEachCall
|
||||
*/
|
||||
public function __call(string $name, array $arguments): self
|
||||
{
|
||||
|
||||
if (method_exists(TestCall::class, $name)) {
|
||||
$this->testCallProxies->add(Backtrace::file(), Backtrace::line(), $name, $arguments);
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Pest\PendingCalls;
|
||||
|
||||
use Closure;
|
||||
use Pest\PendingCalls;
|
||||
use Pest\Support\Backtrace;
|
||||
use Pest\TestSuite;
|
||||
|
||||
/**
|
||||
@ -13,6 +13,11 @@ use Pest\TestSuite;
|
||||
*/
|
||||
final class DescribeCall
|
||||
{
|
||||
/**
|
||||
* The current describe call.
|
||||
*/
|
||||
private static ?string $describing = null;
|
||||
|
||||
/**
|
||||
* Creates a new Pending Call.
|
||||
*/
|
||||
@ -25,9 +30,26 @@ final class DescribeCall
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the current describing.
|
||||
*/
|
||||
public static function describing(): ?string
|
||||
{
|
||||
return self::$describing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Call.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
PendingCalls::endDescribe($this);
|
||||
self::$describing = $this->description;
|
||||
|
||||
try {
|
||||
($this->tests)();
|
||||
} finally {
|
||||
self::$describing = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,12 +57,14 @@ final class DescribeCall
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
*/
|
||||
public function __call(string $name, array $arguments): self
|
||||
public function __call(string $name, array $arguments): BeforeEachCall
|
||||
{
|
||||
foreach (PendingCalls::$testCalls as [$testCall]) {
|
||||
$testCall->{$name}(...$arguments); // @phpstan-ignore-line
|
||||
}
|
||||
$filename = Backtrace::file();
|
||||
|
||||
return $this;
|
||||
$beforeEachCall = new BeforeEachCall(TestSuite::getInstance(), $filename);
|
||||
|
||||
$beforeEachCall->describing = $this->description;
|
||||
|
||||
return $beforeEachCall->{$name}(...$arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ use Pest\Factories\Covers\CoversClass;
|
||||
use Pest\Factories\Covers\CoversFunction;
|
||||
use Pest\Factories\Covers\CoversNothing;
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
use Pest\PendingCalls;
|
||||
use Pest\PendingCalls\Concerns\Describable;
|
||||
use Pest\Plugins\Only;
|
||||
use Pest\Support\Backtrace;
|
||||
@ -52,6 +51,8 @@ final class TestCall
|
||||
|
||||
$this->descriptionLess = $description === null;
|
||||
|
||||
$this->describing = DescribeCall::describing();
|
||||
|
||||
$this->testSuite->beforeEach->get($this->filename)[0]($this);
|
||||
}
|
||||
|
||||
@ -344,10 +345,11 @@ final class TestCall
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
PendingCalls::test($this, function () {
|
||||
if ($this->describing) {
|
||||
$this->testCaseMethod->description = '`'.$this->describing.'` '.$this->testCaseMethod->description;
|
||||
$this->testCaseMethod->describing = $this->describing;
|
||||
}
|
||||
|
||||
$this->testSuite->tests->set($this->testCaseMethod);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ final class AfterEachRepository
|
||||
if (array_key_exists($filename, $this->state)) {
|
||||
$fromAfterEachTestCase = $this->state[$filename];
|
||||
|
||||
$afterEachTestCase = ChainableClosure::from($fromAfterEachTestCase, $afterEachTestCase)
|
||||
$afterEachTestCase = ChainableClosure::fromSameObject($fromAfterEachTestCase, $afterEachTestCase)
|
||||
->bindTo($afterEachCall, $afterEachCall::class);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ final class AfterEachRepository
|
||||
{
|
||||
$afterEach = $this->state[$filename] ?? NullClosure::create();
|
||||
|
||||
return ChainableClosure::from(function (): void {
|
||||
return ChainableClosure::fromSameObject(function (): void {
|
||||
if (class_exists(Mockery::class)) {
|
||||
if ($container = Mockery::getContainer()) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
|
||||
@ -27,8 +27,8 @@ final class BeforeEachRepository
|
||||
if (array_key_exists($filename, $this->state)) {
|
||||
[$fromBeforeEachTestCall, $fromBeforeEachTestCase] = $this->state[$filename];
|
||||
|
||||
$beforeEachTestCall = ChainableClosure::from($fromBeforeEachTestCall, $beforeEachTestCall)->bindTo($beforeEachCall, $beforeEachCall::class);
|
||||
$beforeEachTestCase = ChainableClosure::from($fromBeforeEachTestCase, $beforeEachTestCase)->bindTo($beforeEachCall, $beforeEachCall::class);
|
||||
$beforeEachTestCall = ChainableClosure::fromDifferentObjects($fromBeforeEachTestCall, $beforeEachTestCall);
|
||||
$beforeEachTestCase = ChainableClosure::fromSameObject($fromBeforeEachTestCase, $beforeEachTestCase)->bindTo($beforeEachCall, $beforeEachCall::class);
|
||||
}
|
||||
|
||||
assert($beforeEachTestCall instanceof Closure);
|
||||
|
||||
@ -31,7 +31,7 @@ final class ChainableClosure
|
||||
/**
|
||||
* Calls the given `$closure` and chains the `$next` closure.
|
||||
*/
|
||||
public static function from(Closure $closure, Closure $next): Closure
|
||||
public static function fromSameObject(Closure $closure, Closure $next): Closure
|
||||
{
|
||||
return function () use ($closure, $next): void {
|
||||
if (! is_object($this)) { // @phpstan-ignore-line
|
||||
@ -43,6 +43,17 @@ final class ChainableClosure
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the given `$closure` and chains the `$next` closure.
|
||||
*/
|
||||
public static function fromDifferentObjects(Closure $closure, Closure $next): Closure
|
||||
{
|
||||
return function () use ($closure, $next): void {
|
||||
$closure(...func_get_args());
|
||||
$next(...func_get_args());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the given static `$closure` and chains the `$next` closure.
|
||||
*/
|
||||
|
||||
@ -6,56 +6,73 @@ test('before each', function () {
|
||||
expect($this->count)->toBe(1);
|
||||
});
|
||||
|
||||
describe('describable', function () {
|
||||
describe('hooks', function () {
|
||||
beforeEach(function () {
|
||||
$this->count++;
|
||||
});
|
||||
|
||||
test('basic', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('before each', function () {
|
||||
test('value', function () {
|
||||
expect($this->count)->toBe(2);
|
||||
$this->count++;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect($this->count)->toBe(2);
|
||||
expect($this->count)->toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('another describable', function () {
|
||||
describe('hooks in different orders', function () {
|
||||
beforeEach(function () {
|
||||
$this->count++;
|
||||
});
|
||||
|
||||
test('basic', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('before each', function () {
|
||||
expect($this->count)->toBe(2);
|
||||
test('value', function () {
|
||||
expect($this->count)->toBe(3);
|
||||
$this->count++;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect($this->count)->toBe(2);
|
||||
expect($this->count)->toBe(4);
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
$this->count++;
|
||||
});
|
||||
});
|
||||
|
||||
test('should not fail')->todo()->shouldNotRun();
|
||||
test('todo')->todo()->shouldNotRun();
|
||||
|
||||
test('previous describable before each does not get applied here', function () {
|
||||
expect($this->count)->toBe(1);
|
||||
});
|
||||
|
||||
afterEach(fn () => expect($this->count)->toBe(is_null($this->__describeDescription) ? 1 : 2));
|
||||
|
||||
describe('todo', function () {
|
||||
describe('todo on hook', function () {
|
||||
beforeEach()->todo();
|
||||
|
||||
test('should not fail')->shouldNotRun();
|
||||
test('should run')->expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
describe('todo after describe', function () {
|
||||
describe('todo on describe', function () {
|
||||
test('should not fail')->shouldNotRun();
|
||||
|
||||
test('should run')->expect(true)->toBeTrue();
|
||||
})->todo();
|
||||
|
||||
test('should run')->expect(true)->toBeTrue();
|
||||
|
||||
test('with', fn ($foo) => expect($foo)->toBe(1))->with([1]);
|
||||
|
||||
describe('with on hook', function () {
|
||||
beforeEach()->with([2]);
|
||||
|
||||
test('value', function ($foo) {
|
||||
expect($foo)->toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with on describe', function () {
|
||||
test('value', function ($foo) {
|
||||
expect($foo)->toBe(3);
|
||||
});
|
||||
})->with([3]);
|
||||
|
||||
Reference in New Issue
Block a user