Merge branch 'master' into next-1

This commit is contained in:
Fabio Ivona
2021-11-29 09:36:07 +01:00
committed by GitHub
16 changed files with 274 additions and 97 deletions

View File

@ -15,7 +15,7 @@ jobs:
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: 8.0 php-version: 8.1
tools: composer:v2 tools: composer:v2
coverage: none coverage: none
@ -23,7 +23,7 @@ jobs:
run: composer update --no-interaction --no-progress run: composer update --no-interaction --no-progress
- name: Run PHP-CS-Fixer - name: Run PHP-CS-Fixer
run: vendor/bin/php-cs-fixer fix -v --allow-risky=yes --dry-run run: PHP_CS_FIXER_IGNORE_ENV=true vendor/bin/php-cs-fixer fix -v --allow-risky=yes --dry-run
phpstan: phpstan:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -40,7 +40,7 @@ jobs:
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: 8.0 php-version: 8.1
tools: composer:v2 tools: composer:v2
coverage: none coverage: none
@ -48,4 +48,4 @@ jobs:
run: composer update --prefer-stable --no-interaction --no-progress run: composer update --prefer-stable --no-interaction --no-progress
- name: Run PHPStan - name: Run PHPStan
run: vendor/bin/phpstan analyse --no-progress run: vendor/bin/phpstan analyse --no-progress --debug

View File

@ -60,8 +60,8 @@
"bin/pest" "bin/pest"
], ],
"scripts": { "scripts": {
"lint": "php-cs-fixer fix -v", "lint": "PHP_CS_FIXER_IGNORE_ENV=true php-cs-fixer fix -v",
"test:lint": "php-cs-fixer fix -v --dry-run", "test:lint": "PHP_CS_FIXER_IGNORE_ENV=true php-cs-fixer fix -v --dry-run",
"test:types": "phpstan analyse --ansi --memory-limit=-1 --debug", "test:types": "phpstan analyse --ansi --memory-limit=-1 --debug",
"test:unit": "php bin/pest --colors=always --exclude-group=integration", "test:unit": "php bin/pest --colors=always --exclude-group=integration",
"test:parallel": "exit 1", "test:parallel": "exit 1",

View File

@ -9,7 +9,6 @@ parameters:
- src - src
checkMissingIterableValueType: true checkMissingIterableValueType: true
checkGenericClassInNonGenericObjectType: false
reportUnmatchedIgnoredErrors: true reportUnmatchedIgnoredErrors: true
ignoreErrors: ignoreErrors:

View File

@ -14,6 +14,8 @@ trait RetrievesValues
* *
* Safely retrieve the value at the given key from an object or array. * Safely retrieve the value at the given key from an object or array.
* *
* @template TRetrievableValue
*
* @param array<string, TRetrievableValue>|object $value * @param array<string, TRetrievableValue>|object $value
* @param TRetrievableValue|null $default * @param TRetrievableValue|null $default
* *

View File

@ -7,6 +7,7 @@ namespace Pest\Concerns;
use Closure; use Closure;
use Pest\Support\ChainableClosure; use Pest\Support\ChainableClosure;
use Pest\Support\ExceptionTrace; use Pest\Support\ExceptionTrace;
use Pest\Support\Reflection;
use Pest\TestSuite; use Pest\TestSuite;
use Throwable; use Throwable;
@ -210,7 +211,28 @@ trait Testable
*/ */
private function __resolveTestArguments(array $arguments): array private function __resolveTestArguments(array $arguments): array
{ {
return array_map(fn ($data) => $data instanceof Closure ? $this->__callClosure($data, []) : $data, $arguments); if (count($arguments) !== 1) {
return $arguments;
}
if (!$arguments[0] instanceof Closure) {
return $arguments;
}
$underlyingTest = Reflection::getFunctionVariable($this->__test, 'closure');
$testParameterTypes = array_values(Reflection::getFunctionArguments($underlyingTest));
if (in_array($testParameterTypes[0], ['Closure', 'callable'])) {
return $arguments;
}
$boundDatasetResult = $this->__callClosure($arguments[0], []);
if (count($testParameterTypes) === 1 || !is_array($boundDatasetResult)) {
return [$boundDatasetResult];
}
return array_values($boundDatasetResult);
} }
/** /**

View File

@ -7,7 +7,9 @@ namespace Pest;
/** /**
* @internal * @internal
* *
* @mixin Expectation * @template TValue
*
* @mixin Expectation<TValue>
*/ */
final class Each final class Each
{ {
@ -15,14 +17,21 @@ final class Each
/** /**
* Creates an expectation on each item of the iterable "value". * Creates an expectation on each item of the iterable "value".
*
* @param Expectation<TValue> $original
*/ */
public function __construct(private Expectation $original) public function __construct(private Expectation $original)
{ {
// ..
} }
/** /**
* Creates a new expectation. * Creates a new expectation.
*
* @template TAndValue
*
* @param TAndValue $value
*
* @return Expectation<TAndValue>
*/ */
public function and(mixed $value): Expectation public function and(mixed $value): Expectation
{ {
@ -31,6 +40,8 @@ final class Each
/** /**
* Creates the opposite expectation for the value. * Creates the opposite expectation for the value.
*
* @return self<TValue>
*/ */
public function not(): Each public function not(): Each
{ {
@ -43,6 +54,8 @@ final class Each
* Dynamically calls methods on the class with the given arguments on each item. * Dynamically calls methods on the class with the given arguments on each item.
* *
* @param array<int|string, mixed> $arguments * @param array<int|string, mixed> $arguments
*
* @return self<TValue>
*/ */
public function __call(string $name, array $arguments): Each public function __call(string $name, array $arguments): Each
{ {
@ -58,6 +71,8 @@ final class Each
/** /**
* Dynamically calls methods on the class without any arguments on each item. * Dynamically calls methods on the class without any arguments on each item.
*
* @return self<TValue>
*/ */
public function __get(string $name): Each public function __get(string $name): Each
{ {

View File

@ -45,9 +45,11 @@ final class Expectation
/** /**
* Creates a new expectation. * Creates a new expectation.
* *
* @param TValue $value * @template TAndValue
* *
* @return Expectation<TValue> * @param TAndValue $value
*
* @return self<TAndValue>
*/ */
public function and(mixed $value): Expectation public function and(mixed $value): Expectation
{ {
@ -56,6 +58,8 @@ final class Expectation
/** /**
* Creates a new expectation with the decoded JSON value. * Creates a new expectation with the decoded JSON value.
*
* @return self<mixed>
*/ */
public function json(): Expectation public function json(): Expectation
{ {
@ -84,6 +88,8 @@ final class Expectation
/** /**
* Send the expectation value to Ray along with all given arguments. * Send the expectation value to Ray along with all given arguments.
*
* @return self<TValue>
*/ */
public function ray(mixed ...$arguments): self public function ray(mixed ...$arguments): self
{ {
@ -96,6 +102,8 @@ final class Expectation
/** /**
* Creates the opposite expectation for the value. * Creates the opposite expectation for the value.
*
* @return OppositeExpectation<TValue>
*/ */
public function not(): OppositeExpectation public function not(): OppositeExpectation
{ {
@ -104,6 +112,8 @@ final class Expectation
/** /**
* Creates an expectation on each item of the iterable "value". * Creates an expectation on each item of the iterable "value".
*
* @return Each<TValue>
*/ */
public function each(callable $callback = null): Each public function each(callable $callback = null): Each
{ {
@ -125,7 +135,9 @@ final class Expectation
* *
* @template TSequenceValue * @template TSequenceValue
* *
* @param (callable(self, self): void)|TSequenceValue ...$callbacks * @param (callable(self<TValue>, self<string|int>): void)|TSequenceValue ...$callbacks
*
* @return self<TValue>
*/ */
public function sequence(mixed ...$callbacks): Expectation public function sequence(mixed ...$callbacks): Expectation
{ {
@ -167,15 +179,13 @@ final class Expectation
* @template TMatchSubject of array-key * @template TMatchSubject of array-key
* *
* @param (callable(): TMatchSubject)|TMatchSubject $subject * @param (callable(): TMatchSubject)|TMatchSubject $subject
* @param array<TMatchSubject, (callable(Expectation<TValue>): mixed)|TValue> $expressions * @param array<TMatchSubject, (callable(self<TValue>): mixed)|TValue> $expressions
*
* @return self<TValue>
*/ */
public function match(mixed $subject, array $expressions): Expectation public function match(mixed $subject, array $expressions): Expectation
{ {
$subject = is_callable($subject) $subject = $subject instanceof Closure ? $subject() : $subject;
? $subject
: fn () => $subject;
$subject = $subject();
$matched = false; $matched = false;
@ -208,6 +218,8 @@ final class Expectation
* *
* @param (callable(): bool)|bool $condition * @param (callable(): bool)|bool $condition
* @param callable(Expectation<TValue>): mixed $callback * @param callable(Expectation<TValue>): mixed $callback
*
* @return self<TValue>
*/ */
public function unless(callable|bool $condition, callable $callback): Expectation public function unless(callable|bool $condition, callable $callback): Expectation
{ {
@ -224,7 +236,9 @@ final class Expectation
* Apply the callback if the given "condition" is truthy. * Apply the callback if the given "condition" is truthy.
* *
* @param (callable(): bool)|bool $condition * @param (callable(): bool)|bool $condition
* @param callable(Expectation<TValue>): mixed $callback * @param callable(self<TValue>): mixed $callback
*
* @return self<TValue>
*/ */
public function when(callable|bool $condition, callable $callback): Expectation public function when(callable|bool $condition, callable $callback): Expectation
{ {

View File

@ -7,7 +7,6 @@ namespace Pest\Factories;
use ParseError; use ParseError;
use Pest\Concerns; use Pest\Concerns;
use Pest\Contracts\HasPrintableTestCaseName; use Pest\Contracts\HasPrintableTestCaseName;
use Pest\Datasets;
use Pest\Exceptions\DatasetMissing; use Pest\Exceptions\DatasetMissing;
use Pest\Exceptions\ShouldNotHappen; use Pest\Exceptions\ShouldNotHappen;
use Pest\Exceptions\TestAlreadyExist; use Pest\Exceptions\TestAlreadyExist;
@ -73,9 +72,10 @@ final class TestCaseFactory
{ {
$methodsUsingOnly = $this->methodsUsingOnly(); $methodsUsingOnly = $this->methodsUsingOnly();
$methods = array_values(array_filter($this->methods, function ($method) use ($methodsUsingOnly) { $methods = array_values(array_filter(
return count($methodsUsingOnly) === 0 || in_array($method, $methodsUsingOnly, true); $this->methods,
})); fn ($method) => count($methodsUsingOnly) === 0 || in_array($method, $methodsUsingOnly, true)
));
if (count($methods) > 0) { if (count($methods) > 0) {
$this->evaluate($this->filename, $methods); $this->evaluate($this->filename, $methods);
@ -141,57 +141,11 @@ final class TestCaseFactory
$classFQN .= $className; $classFQN .= $className;
} }
$methodsCode = implode('', array_map(static function (TestCaseMethodFactory $method): string { $methodsCode = implode('', array_map(
if ($method->description === null) { fn (TestCaseMethodFactory $methodFactory) => $methodFactory->buildForEvaluation(self::$annotations),
throw ShouldNotHappen::fromMessage('The test description may not be empty.'); $methods
}
$methodName = Str::evaluable($method->description);
$datasetsCode = '';
$annotations = ['@test'];
foreach (self::$annotations as $annotation) {
/** @phpstan-ignore-next-line */
$annotations = (new $annotation())->__invoke($method, $annotations);
}
if (count($method->datasets) > 0) {
$dataProviderName = $methodName . '_dataset';
$annotations[] = "@dataProvider $dataProviderName";
Datasets::with($method->filename, $methodName, $method->datasets);
$datasetsCode = <<<EOF
public function $dataProviderName()
{
return __PestDatasets::get(self::\$__filename, "$methodName");
}
EOF;
}
$annotations = implode('', array_map(
static fn ($annotation) => sprintf("\n * %s", $annotation), $annotations,
)); ));
return <<<EOF
/**$annotations
*/
public function $methodName()
{
return \$this->__runTest(
\$this->__test,
...func_get_args(),
);
}
$datasetsCode
EOF;
}, $methods));
try { try {
eval(" eval("
namespace $namespace; namespace $namespace;

View File

@ -5,8 +5,10 @@ declare(strict_types=1);
namespace Pest\Factories; namespace Pest\Factories;
use Closure; use Closure;
use Pest\Datasets;
use Pest\Exceptions\ShouldNotHappen; use Pest\Exceptions\ShouldNotHappen;
use Pest\Factories\Concerns\HigherOrderable; use Pest\Factories\Concerns\HigherOrderable;
use Pest\Support\Str;
use Pest\TestSuite; use Pest\TestSuite;
use PHPUnit\Framework\Assert; use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -17,6 +19,7 @@ use PHPUnit\Framework\TestCase;
final class TestCaseMethodFactory final class TestCaseMethodFactory
{ {
use HigherOrderable; use HigherOrderable;
/** /**
* Determines if the Test Case will be the "only" being run. * Determines if the Test Case will be the "only" being run.
*/ */
@ -51,11 +54,9 @@ final class TestCaseMethodFactory
public ?string $description, public ?string $description,
public ?Closure $closure, public ?Closure $closure,
) { ) {
if ($this->closure === null) { $this->closure ??= function () {
$this->closure = function () {
Assert::getCount() > 0 ?: self::markTestIncomplete(); // @phpstan-ignore-line Assert::getCount() > 0 ?: self::markTestIncomplete(); // @phpstan-ignore-line
}; };
}
$this->bootHigherOrderable(); $this->bootHigherOrderable();
} }
@ -100,4 +101,68 @@ final class TestCaseMethodFactory
{ {
return count($this->datasets) > 0 || count($this->depends) > 0; return count($this->datasets) > 0 || count($this->depends) > 0;
} }
/**
* Creates a PHPUnit method as a string ready for evaluation.
*
* @param array<int, class-string> $annotationsToUse
*/
public function buildForEvaluation(array $annotationsToUse): string
{
if ($this->description === null) {
throw ShouldNotHappen::fromMessage('The test description may not be empty.');
}
$methodName = Str::evaluable($this->description);
$datasetsCode = '';
$annotations = ['@test'];
foreach ($annotationsToUse as $annotation) {
/** @phpstan-ignore-next-line */
$annotations = (new $annotation())->__invoke($this, $annotations);
}
if (count($this->datasets) > 0) {
$dataProviderName = $methodName . '_dataset';
$annotations[] = "@dataProvider $dataProviderName";
$datasetsCode = $this->buildDatasetForEvaluation($methodName, $dataProviderName);
}
$annotations = implode('', array_map(
static fn ($annotation) => sprintf("\n * %s", $annotation), $annotations,
));
return <<<EOF
/**$annotations
*/
public function $methodName()
{
return \$this->__runTest(
\$this->__test,
...func_get_args(),
);
}
$datasetsCode
EOF;
}
/**
* Creates a PHPUnit Data Provider as a string ready for evaluation.
*/
private function buildDatasetForEvaluation(string $methodName, string $dataProviderName): string
{
Datasets::with($this->filename, $methodName, $this->datasets);
return <<<EOF
public function $dataProviderName()
{
return __PestDatasets::get(self::\$__filename, "$methodName");
}
EOF;
}
} }

View File

@ -18,7 +18,11 @@ if (!function_exists('expect')) {
/** /**
* Creates a new expectation. * Creates a new expectation.
* *
* @param mixed $value the Value * @template TValue
*
* @param TValue $value the Value
*
* @return Expectation<TValue>|Extendable
*/ */
function expect($value = null): Expectation|Extendable function expect($value = null): Expectation|Extendable
{ {

View File

@ -4,19 +4,23 @@ declare(strict_types=1);
namespace Pest; namespace Pest;
use Pest\Concerns\Expectable;
use Pest\Concerns\RetrievesValues; use Pest\Concerns\RetrievesValues;
/** /**
* @internal * @internal
* *
* @mixin Expectation * @template TOriginalValue
* @template TValue
*
* @mixin Expectation<TOriginalValue>
*/ */
final class HigherOrderExpectation final class HigherOrderExpectation
{ {
use Expectable;
use RetrievesValues; use RetrievesValues;
/**
* @var Expectation<TValue>|Each<TValue>
*/
private Expectation|Each $expectation; private Expectation|Each $expectation;
private bool $opposite = false; private bool $opposite = false;
@ -25,6 +29,9 @@ final class HigherOrderExpectation
/** /**
* Creates a new higher order expectation. * Creates a new higher order expectation.
*
* @param Expectation<TOriginalValue> $original
* @param TValue $value
*/ */
public function __construct(private Expectation $original, mixed $value) public function __construct(private Expectation $original, mixed $value)
{ {
@ -33,6 +40,8 @@ final class HigherOrderExpectation
/** /**
* Creates the opposite expectation for the value. * Creates the opposite expectation for the value.
*
* @return self<TOriginalValue, TValue>
*/ */
public function not(): HigherOrderExpectation public function not(): HigherOrderExpectation
{ {
@ -41,14 +50,28 @@ final class HigherOrderExpectation
return $this; return $this;
} }
/**
* Creates a new Expectation.
*
* @template TExpectValue
*
* @param TExpectValue $value
*
* @return Expectation<TExpectValue>
*/
public function expect(mixed $value): Expectation
{
return new Expectation($value);
}
/** /**
* Creates a new expectation. * Creates a new expectation.
* *
* @template TValue * @template TExpectValue
* *
* @param TValue $value * @param TExpectValue $value
* *
* @return Expectation<TValue> * @return Expectation<TExpectValue>
*/ */
public function and(mixed $value): Expectation public function and(mixed $value): Expectation
{ {
@ -59,6 +82,8 @@ final class HigherOrderExpectation
* Dynamically calls methods on the class with the given arguments. * Dynamically calls methods on the class with the given arguments.
* *
* @param array<int, mixed> $arguments * @param array<int, mixed> $arguments
*
* @return self<TOriginalValue, mixed>|self<TOriginalValue, TValue>
*/ */
public function __call(string $name, array $arguments): self public function __call(string $name, array $arguments): self
{ {
@ -72,6 +97,8 @@ final class HigherOrderExpectation
/** /**
* Accesses properties in the value or in the expectation. * Accesses properties in the value or in the expectation.
*
* @return self<TOriginalValue, mixed>|self<TOriginalValue, TValue>
*/ */
public function __get(string $name): self public function __get(string $name): self
{ {
@ -99,9 +126,12 @@ final class HigherOrderExpectation
/** /**
* Retrieve the applicable value based on the current reset condition. * Retrieve the applicable value based on the current reset condition.
*
* @return TOriginalValue|TValue
*/ */
private function getValue(): mixed private function getValue(): mixed
{ {
// @phpstan-ignore-next-line
return $this->shouldReset ? $this->original->value : $this->expectation->value; return $this->shouldReset ? $this->original->value : $this->expectation->value;
} }
@ -109,6 +139,8 @@ final class HigherOrderExpectation
* Performs the given assertion with the current expectation. * Performs the given assertion with the current expectation.
* *
* @param array<int, mixed> $arguments * @param array<int, mixed> $arguments
*
* @return self<TOriginalValue, TValue>
*/ */
private function performAssertion(string $name, array $arguments): self private function performAssertion(string $name, array $arguments): self
{ {

View File

@ -10,29 +10,34 @@ use SebastianBergmann\Exporter\Exporter;
/** /**
* @internal * @internal
* *
* @mixin Expectation * @template TValue
*
* @mixin Expectation<TValue>
*/ */
final class OppositeExpectation final class OppositeExpectation
{ {
/** /**
* Creates a new opposite expectation. * Creates a new opposite expectation.
*
* @param Expectation<TValue> $original
*/ */
public function __construct(private Expectation $original) public function __construct(private Expectation $original)
{ {
// ..
} }
/** /**
* Asserts that the value array not has the provided $keys. * Asserts that the value array not has the provided $keys.
* *
* @param array<int, int|string> $keys * @param array<int, int|string> $keys
*
* @return Expectation<TValue>
*/ */
public function toHaveKeys(array $keys): Expectation public function toHaveKeys(array $keys): Expectation
{ {
foreach ($keys as $key) { foreach ($keys as $key) {
try { try {
$this->original->toHaveKey($key); $this->original->toHaveKey($key);
} catch (ExpectationFailedException $exception) { } catch (ExpectationFailedException) {
continue; continue;
} }
@ -47,14 +52,14 @@ final class OppositeExpectation
* *
* @param array<int, mixed> $arguments * @param array<int, mixed> $arguments
* *
* @return Expectation|never * @return Expectation<TValue>|Expectation<mixed>|never
*/ */
public function __call(string $name, array $arguments): Expectation public function __call(string $name, array $arguments): Expectation
{ {
try { try {
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
$this->original->{$name}(...$arguments); $this->original->{$name}(...$arguments);
} catch (ExpectationFailedException $exception) { } catch (ExpectationFailedException) {
return $this->original; return $this->original;
} }
@ -64,13 +69,13 @@ final class OppositeExpectation
/** /**
* Handle dynamic properties gets into the original expectation. * Handle dynamic properties gets into the original expectation.
* *
* @return Expectation|never * @return Expectation<TValue>|Expectation<mixed>|never
*/ */
public function __get(string $name): Expectation public function __get(string $name): Expectation
{ {
try { try {
$this->original->{$name}; // @phpstan-ignore-line $this->original->{$name}; // @phpstan-ignore-line
} catch (ExpectationFailedException $exception) { // @phpstan-ignore-line } catch (ExpectationFailedException) { // @phpstan-ignore-line
return $this->original; return $this->original;
} }

View File

@ -38,6 +38,10 @@ final class HigherOrderMessage
/** /**
* Re-throws the given `$throwable` with the good line and filename. * Re-throws the given `$throwable` with the good line and filename.
*
* @template TValue of object
*
* @param TValue $target
*/ */
public function call(object $target): mixed public function call(object $target): mixed
{ {
@ -59,7 +63,7 @@ final class HigherOrderMessage
Reflection::setPropertyValue($throwable, 'line', $this->line); Reflection::setPropertyValue($throwable, 'line', $this->line);
if ($throwable->getMessage() === self::getUndefinedMethodMessage($target, $this->name)) { if ($throwable->getMessage() === self::getUndefinedMethodMessage($target, $this->name)) {
/** @var ReflectionClass $reflection */ /** @var ReflectionClass<TValue> $reflection */
$reflection = new ReflectionClass($target); $reflection = new ReflectionClass($target);
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
$reflection = $reflection->getParentClass() ?: $reflection; $reflection = $reflection->getParentClass() ?: $reflection;

View File

@ -118,11 +118,14 @@ final class Reflection
/** /**
* Sets the property value of the given object. * Sets the property value of the given object.
* *
* @template TValue of object
*
* @param TValue $object
* @param mixed $value * @param mixed $value
*/ */
public static function setPropertyValue(object $object, string $property, $value): void public static function setPropertyValue(object $object, string $property, $value): void
{ {
/** @var ReflectionClass $reflectionClass */ /** @var ReflectionClass<TValue> $reflectionClass */
$reflectionClass = new ReflectionClass($object); $reflectionClass = new ReflectionClass($object);
$reflectionProperty = null; $reflectionProperty = null;
@ -202,4 +205,12 @@ final class Reflection
return $arguments; return $arguments;
} }
/**
* @return mixed
*/
public static function getFunctionVariable(Closure $function, string $key)
{
return (new ReflectionFunction($function))->getStaticVariables()[$key] ?? null;
}
} }

View File

@ -96,11 +96,20 @@
✓ more than two datasets with (2) / (4) / (5) ✓ more than two datasets with (2) / (4) / (5)
✓ more than two datasets with (2) / (4) / (6) ✓ more than two datasets with (2) / (4) / (6)
✓ more than two datasets did the job right ✓ more than two datasets did the job right
✓ it can resolve a dataset after the test case is available with (Closure Object (...)) ✓ it can resolve a dataset after the test case is available with (Closure Object (...)) #1
✓ it can resolve a dataset after the test case is available with (Closure Object (...)) #2
✓ it can resolve a dataset after the test case is available with shared yield sets with (Closure Object (...)) #1 ✓ it can resolve a dataset after the test case is available with shared yield sets with (Closure Object (...)) #1
✓ it can resolve a dataset after the test case is available with shared yield sets with (Closure Object (...)) #2 ✓ it can resolve a dataset after the test case is available with shared yield sets with (Closure Object (...)) #2
✓ it can resolve a dataset after the test case is available with shared array sets with (Closure Object (...)) #1 ✓ it can resolve a dataset after the test case is available with shared array sets with (Closure Object (...)) #1
✓ it can resolve a dataset after the test case is available with shared array sets with (Closure Object (...)) #2 ✓ it can resolve a dataset after the test case is available with shared array sets with (Closure Object (...)) #2
✓ it resolves a potential bound dataset logically with ('foo', Closure Object (...))
✓ it resolves a potential bound dataset logically even when the closure comes first with (Closure Object (...), 'bar')
✓ it will not resolve a closure if it is type hinted as a closure with (Closure Object (...)) #1
✓ it will not resolve a closure if it is type hinted as a closure with (Closure Object (...)) #2
✓ it will not resolve a closure if it is type hinted as a callable with (Closure Object (...)) #1
✓ it will not resolve a closure if it is type hinted as a callable with (Closure Object (...)) #2
✓ it can correctly resolve a bound dataset that returns an array with (Closure Object (...))
✓ it can correctly resolve a bound dataset that returns an array but wants to be spread with (Closure Object (...))
PASS Tests\Features\Exceptions PASS Tests\Features\Exceptions
✓ it gives access the the underlying expectException ✓ it gives access the the underlying expectException

View File

@ -234,6 +234,7 @@ it('can resolve a dataset after the test case is available', function ($result)
expect($result)->toBe('bar'); expect($result)->toBe('bar');
})->with([ })->with([
function () { return $this->foo; }, function () { return $this->foo; },
[function () { return $this->foo; }],
]); ]);
it('can resolve a dataset after the test case is available with shared yield sets', function ($result) { it('can resolve a dataset after the test case is available with shared yield sets', function ($result) {
@ -243,3 +244,43 @@ it('can resolve a dataset after the test case is available with shared yield set
it('can resolve a dataset after the test case is available with shared array sets', function ($result) { it('can resolve a dataset after the test case is available with shared array sets', function ($result) {
expect($result)->toBeInt()->toBeLessThan(3); expect($result)->toBeInt()->toBeLessThan(3);
})->with('bound.array'); })->with('bound.array');
it('resolves a potential bound dataset logically', function ($foo, $bar) {
expect($foo)->toBe('foo');
expect($bar())->toBe('bar');
})->with([
['foo', function () { return 'bar'; }], // This should be passed as a closure because we've passed multiple arguments
]);
it('resolves a potential bound dataset logically even when the closure comes first', function ($foo, $bar) {
expect($foo())->toBe('foo');
expect($bar)->toBe('bar');
})->with([
[function () { return 'foo'; }, 'bar'], // This should be passed as a closure because we've passed multiple arguments
]);
it('will not resolve a closure if it is type hinted as a closure', function (Closure $data) {
expect($data())->toBeString();
})->with([
function () { return 'foo'; },
function () { return 'bar'; },
]);
it('will not resolve a closure if it is type hinted as a callable', function (callable $data) {
expect($data())->toBeString();
})->with([
function () { return 'foo'; },
function () { return 'bar'; },
]);
it('can correctly resolve a bound dataset that returns an array', function (array $data) {
expect($data)->toBe(['foo', 'bar', 'baz']);
})->with([
function () { return ['foo', 'bar', 'baz']; },
]);
it('can correctly resolve a bound dataset that returns an array but wants to be spread', function (string $foo, string $bar, string $baz) {
expect([$foo, $bar, $baz])->toBe(['foo', 'bar', 'baz']);
})->with([
function () { return ['foo', 'bar', 'baz']; },
]);