merge from master

This commit is contained in:
Fabio Ivona
2021-11-15 11:54:42 +01:00
104 changed files with 1452 additions and 2286 deletions

View File

@ -14,13 +14,13 @@ trait Expectable
/**
* @template TValue
*
* Creates a new expectation.
* Creates a new Expectation.
*
* @param TValue $value
*
* @return Expectation<TValue>
*/
public function expect($value): Expectation
public function expect(mixed $value): Expectation
{
return new Expectation($value);
}

View File

@ -6,7 +6,6 @@ namespace Pest\Concerns;
use BadMethodCallException;
use Closure;
use Pest\Expectation;
/**
* @internal
@ -14,15 +13,17 @@ use Pest\Expectation;
trait Extendable
{
/**
* The list of extends.
*
* @var array<string, Closure>
*/
private static $extends = [];
private static array $extends = [];
/** @var array<string, array<Closure>> */
private static $pipes = [];
private static array $pipes = [];
/**
* Register a custom extend.
* Register a new extend.
*/
public static function extend(string $name, Closure $extend): void
{
@ -30,7 +31,7 @@ trait Extendable
}
/**
* Register a a pipe to be applied before an expectation is checked.
* Register a pipe to be applied before an expectation is checked.
*/
public static function pipe(string $name, Closure $pipe): void
{
@ -39,10 +40,8 @@ trait Extendable
/**
* Recister an interceptor that should replace an existing expectation.
*
* @param string|Closure $filter
*/
public static function intercept(string $name, $filter, Closure $handler): void
public static function intercept(string $name, string|Closure $filter, Closure $handler): void
{
if (is_string($filter)) {
$filter = function ($value) use ($filter): bool {
@ -65,7 +64,7 @@ trait Extendable
}
/**
* Checks if extend is registered.
* Checks if given extend name is registered.
*/
public static function hasExtend(string $name): bool
{
@ -102,10 +101,8 @@ trait Extendable
* Dynamically handle calls to the class.
*
* @param array<int, mixed> $parameters
*
* @return mixed
*/
public function __call(string $method, array $parameters)
public function __call(string $method, array $parameters): mixed
{
if (!static::hasExtend($method)) {
throw new BadMethodCallException("$method is not a callable method name.");

View File

@ -9,21 +9,33 @@ namespace Pest\Concerns\Logging;
*/
trait WritesToConsole
{
/**
* Writes the given success message to the console.
*/
private function writeSuccess(string $message): void
{
$this->writePestTestOutput($message, 'fg-green, bold', '✓');
}
/**
* Writes the given error message to the console.
*/
private function writeError(string $message): void
{
$this->writePestTestOutput($message, 'fg-red, bold', '');
}
/**
* Writes the given warning message to the console.
*/
private function writeWarning(string $message): void
{
$this->writePestTestOutput($message, 'fg-yellow, bold', '-');
}
/**
* Writes the give message to the console.
*/
private function writePestTestOutput(string $message, string $color, string $symbol): void
{
$this->writeWithColor($color, "$symbol ", false);

View File

@ -19,7 +19,7 @@ trait RetrievesValues
*
* @return TRetrievableValue|null
*/
private function retrieve(string $key, $value, $default = null)
private function retrieve(string $key, mixed $value, mixed $default = null): mixed
{
if (is_array($value)) {
return $value[$key] ?? $default;

View File

@ -8,157 +8,105 @@ use Closure;
use Pest\Support\ChainableClosure;
use Pest\Support\ExceptionTrace;
use Pest\TestSuite;
use PHPUnit\Framework\ExecutionOrderDependency;
use Throwable;
/**
* To avoid inheritance conflicts, all the fields related
* to Pest only will be prefixed by double underscore.
*
* @internal
*/
trait Testable
{
/**
* The test case description. Contains the first
* argument of global functions like `it` and `test`.
*
* @var string
* The Test Case "test" closure.
*/
private $__description;
private Closure $__test;
/**
* Holds the test closure function.
*
* @var Closure
* The Test Case "setUp" closure.
*/
private $__test;
private ?Closure $__beforeEach = null;
/**
* Holds a global/shared beforeEach ("set up") closure if one has been
* defined.
*
* @var Closure|null
* The Test Case "tearDown" closure.
*/
private $beforeEach = null;
private ?Closure $__afterEach = null;
/**
* Holds a global/shared afterEach ("tear down") closure if one has been
* defined.
*
* @var Closure|null
* The Test Case "setUpBeforeClass" closure.
*/
private $afterEach = null;
private static ?Closure $__beforeAll = null;
/**
* Holds a global/shared beforeAll ("set up before") closure if one has been
* defined.
*
* @var Closure|null
* The test "tearDownAfterClass" closure.
*/
private static $beforeAll = null;
private static ?Closure $__afterAll = null;
/**
* Holds a global/shared afterAll ("tear down after") closure if one has
* been defined.
*
* @var Closure|null
* Resets the test case static properties.
*/
private static $afterAll = null;
/**
* Creates a new instance of the test case.
*/
public function __construct(Closure $test, string $description, array $data)
public static function flush(): void
{
$this->__test = $test;
$this->__description = $description;
self::$beforeAll = null;
self::$afterAll = null;
parent::__construct('__test', $data);
self::$__beforeAll = null;
self::$__afterAll = null;
}
/**
* Adds the groups to the current test case.
* Creates a new Test Case instance.
*/
public function addGroups(array $groups): void
public function __construct(string $name)
{
$groups = array_unique(array_merge($this->getGroups(), $groups));
parent::__construct($name);
$this->setGroups($groups);
$this->__test = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($name)->getClosure($this);
}
/**
* Add dependencies to the test case and map them to instances of ExecutionOrderDependency.
* Adds a new "setUpBeforeClass" to the Test Case.
*/
public function addDependencies(array $tests): void
{
$className = get_class($this);
$tests = array_map(function (string $test) use ($className): ExecutionOrderDependency {
if (strpos($test, '::') === false) {
$test = "{$className}::{$test}";
}
return new ExecutionOrderDependency($test, null, '');
}, $tests);
$this->setDependencies($tests);
}
/**
* Add a shared/"global" before all test hook that will execute **before**
* the test defined `beforeAll` hook(s).
*/
public function addBeforeAll(?Closure $hook): void
public function __addBeforeAll(?Closure $hook): void
{
if (!$hook) {
return;
}
self::$beforeAll = (self::$beforeAll instanceof Closure)
? ChainableClosure::fromStatic(self::$beforeAll, $hook)
self::$__beforeAll = (self::$__beforeAll instanceof Closure)
? ChainableClosure::fromStatic(self::$__beforeAll, $hook)
: $hook;
}
/**
* Add a shared/"global" after all test hook that will execute **before**
* the test defined `afterAll` hook(s).
* Adds a new "tearDownAfterClass" to the Test Case.
*/
public function addAfterAll(?Closure $hook): void
public function __addAfterAll(?Closure $hook): void
{
if (!$hook) {
return;
}
self::$afterAll = (self::$afterAll instanceof Closure)
? ChainableClosure::fromStatic(self::$afterAll, $hook)
self::$__afterAll = (self::$__afterAll instanceof Closure)
? ChainableClosure::fromStatic(self::$__afterAll, $hook)
: $hook;
}
/**
* Add a shared/"global" before each test hook that will execute **before**
* the test defined `beforeEach` hook.
* Adds a new "setUp" to the Test Case.
*/
public function addBeforeEach(?Closure $hook): void
public function __addBeforeEach(?Closure $hook): void
{
$this->addHook('beforeEach', $hook);
$this->__addHook('__beforeEach', $hook);
}
/**
* Add a shared/"global" after each test hook that will execute **before**
* the test defined `afterEach` hook.
* Adds a new "tearDown" to the Test Case.
*/
public function addAfterEach(?Closure $hook): void
public function __addAfterEach(?Closure $hook): void
{
$this->addHook('afterEach', $hook);
$this->__addHook('__afterEach', $hook);
}
/**
* Add a shared/global hook and compose them if more than one is passed.
* Adds a new "hook" to the Test Case.
*/
private function addHook(string $property, ?Closure $hook): void
private function __addHook(string $property, ?Closure $hook): void
{
if (!$hook) {
return;
@ -170,22 +118,15 @@ trait Testable
}
/**
* Returns the test case name. Note that, in Pest
* we ignore withDataset argument as the description
* already contains the dataset description.
* Gets the Test Case filename.
*/
public function getName(bool $withDataSet = true): string
{
return $this->__description;
}
public static function __getFileName(): string
public static function __getFilename(): string
{
return self::$__filename;
}
/**
* This method is called before the first test of this test class is run.
* This method is called before the first test of this Test Case is run.
*/
public static function setUpBeforeClass(): void
{
@ -193,22 +134,22 @@ trait Testable
$beforeAll = TestSuite::getInstance()->beforeAll->get(self::$__filename);
if (self::$beforeAll instanceof Closure) {
$beforeAll = ChainableClosure::fromStatic(self::$beforeAll, $beforeAll);
if (self::$__beforeAll instanceof Closure) {
$beforeAll = ChainableClosure::fromStatic(self::$__beforeAll, $beforeAll);
}
call_user_func(Closure::bind($beforeAll, null, self::class));
}
/**
* This method is called after the last test of this test class is run.
* This method is called after the last test of this Test Case is run.
*/
public static function tearDownAfterClass(): void
{
$afterAll = TestSuite::getInstance()->afterAll->get(self::$__filename);
if (self::$afterAll instanceof Closure) {
$afterAll = ChainableClosure::fromStatic(self::$afterAll, $afterAll);
if (self::$__afterAll instanceof Closure) {
$afterAll = ChainableClosure::fromStatic(self::$__afterAll, $afterAll);
}
call_user_func(Closure::bind($afterAll, null, self::class));
@ -217,7 +158,7 @@ trait Testable
}
/**
* Gets executed before the test.
* Gets executed before the Test Case.
*/
protected function setUp(): void
{
@ -227,22 +168,22 @@ trait Testable
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename);
if ($this->beforeEach instanceof Closure) {
$beforeEach = ChainableClosure::from($this->beforeEach, $beforeEach);
if ($this->__beforeEach instanceof Closure) {
$beforeEach = ChainableClosure::from($this->__beforeEach, $beforeEach);
}
$this->__callClosure($beforeEach, func_get_args());
}
/**
* Gets executed after the test.
* Gets executed after the Test Case.
*/
protected function tearDown(): void
{
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
if ($this->afterEach instanceof Closure) {
$afterEach = ChainableClosure::from($this->afterEach, $afterEach);
if ($this->__afterEach instanceof Closure) {
$afterEach = ChainableClosure::from($this->__afterEach, $afterEach);
}
$this->__callClosure($afterEach, func_get_args());
@ -253,27 +194,13 @@ trait Testable
}
/**
* Returns the test case as string.
*/
public function toString(): string
{
return \sprintf(
'%s::%s',
self::$__filename,
$this->__description
);
}
/**
* Runs the test.
*
* @return mixed
* Executes the Test Case current test.
*
* @throws Throwable
*/
public function __test()
private function __runTest(Closure $closure, ...$args): mixed
{
return $this->__callClosure($this->__test, $this->resolveTestArguments(func_get_args()));
return $this->__callClosure($closure, $this->__resolveTestArguments($args));
}
/**
@ -281,25 +208,22 @@ trait Testable
*
* @throws Throwable
*/
private function resolveTestArguments(array $arguments): array
private function __resolveTestArguments(array $arguments): array
{
return array_map(function ($data) {
return $data instanceof Closure ? $this->__callClosure($data, []) : $data;
}, $arguments);
return array_map(fn ($data) => $data instanceof Closure ? $this->__callClosure($data, []) : $data, $arguments);
}
/**
* @return mixed
*
* @throws Throwable
*/
private function __callClosure(Closure $closure, array $arguments)
private function __callClosure(Closure $closure, array $arguments): mixed
{
return ExceptionTrace::ensure(function () use ($closure, $arguments) {
return call_user_func_array(Closure::bind($closure, $this, get_class($this)), $arguments);
});
return ExceptionTrace::ensure(fn () => call_user_func_array(Closure::bind($closure, $this, $this::class), $arguments));
}
/**
* Gets the Test Case name that should be used by printers.
*/
public function getPrintableTestCaseName(): string
{
return ltrim(self::class, 'P\\');