mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
Merge branch '2.x' into 2.x-fix-ignored-description-for-lazy-datasets
This commit is contained in:
@ -5,7 +5,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://raw.githubusercontent.com/pestphp/art/master/readme.png" width="600" alt="PEST">
|
<img src="https://raw.githubusercontent.com/pestphp/art/master/readme.png" width="600" alt="PEST">
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/pestphp/pest/actions"><img alt="GitHub Workflow Status (master)" src="https://img.shields.io/github/workflow/status/pestphp/pest/Tests/master"></a>
|
<a href="https://github.com/pestphp/pest/actions"><img alt="GitHub Workflow Status (master)" src="https://img.shields.io/github/actions/workflow/status/pestphp/pest/tests.yml?branch=2.x&label=Tests%202.x"></a>
|
||||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Total Downloads" src="https://img.shields.io/packagist/dt/pestphp/pest"></a>
|
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Total Downloads" src="https://img.shields.io/packagist/dt/pestphp/pest"></a>
|
||||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Latest Version" src="https://img.shields.io/packagist/v/pestphp/pest"></a>
|
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Latest Version" src="https://img.shields.io/packagist/v/pestphp/pest"></a>
|
||||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="License" src="https://img.shields.io/packagist/l/pestphp/pest"></a>
|
<a href="https://packagist.org/packages/pestphp/pest"><img alt="License" src="https://img.shields.io/packagist/l/pestphp/pest"></a>
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^8.1.0",
|
"php": "^8.1.0",
|
||||||
"nunomaduro/collision": "^7.0.0",
|
"nunomaduro/collision": "^7.0.0",
|
||||||
"nunomaduro/termwind": "^1.14.2",
|
"nunomaduro/termwind": "^1.15",
|
||||||
"pestphp/pest-plugin": "^2.0.0",
|
"pestphp/pest-plugin": "^2.0.0",
|
||||||
"phpunit/phpunit": "10.0.x-dev"
|
"phpunit/phpunit": "10.0.x-dev"
|
||||||
},
|
},
|
||||||
@ -51,6 +51,7 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"pestphp/pest-dev-tools": "^2.1.0",
|
"pestphp/pest-dev-tools": "^2.1.0",
|
||||||
|
"pestphp/pest-plugin-arch": "^2.0.0",
|
||||||
"symfony/process": "^6.2.0"
|
"symfony/process": "^6.2.0"
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
@ -77,7 +78,7 @@
|
|||||||
"test:integration": "php bin/pest --colors=always --group=integration -v",
|
"test:integration": "php bin/pest --colors=always --group=integration -v",
|
||||||
"update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always",
|
"update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always",
|
||||||
"test": [
|
"test": [
|
||||||
"@rest:refacto",
|
"@test:refacto",
|
||||||
"@test:lint",
|
"@test:lint",
|
||||||
"@test:types",
|
"@test:types",
|
||||||
"@test:unit",
|
"@test:unit",
|
||||||
|
|||||||
@ -98,14 +98,14 @@ final class TestSuiteLoader
|
|||||||
|
|
||||||
self::$loadedClasses = array_merge($loadedClasses, self::$loadedClasses);
|
self::$loadedClasses = array_merge($loadedClasses, self::$loadedClasses);
|
||||||
|
|
||||||
if (empty(self::$loadedClasses)) {
|
if (empty($loadedClasses)) {
|
||||||
return $this->exceptionFor($suiteClassName, $suiteClassFile);
|
return $this->exceptionFor($suiteClassName, $suiteClassFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
$testCaseFound = false;
|
$testCaseFound = false;
|
||||||
|
|
||||||
foreach (self::$loadedClasses as $loadedClass) {
|
foreach (array_reverse($loadedClasses) as $loadedClass) {
|
||||||
if (is_subclass_of($loadedClass, HasPrintableTestCaseName::class)) {
|
if (is_subclass_of($loadedClass, HasPrintableTestCaseName::class) || is_subclass_of($loadedClass, TestCase::class)) {
|
||||||
$suiteClassName = $loadedClass;
|
$suiteClassName = $loadedClass;
|
||||||
|
|
||||||
$testCaseFound = true;
|
$testCaseFound = true;
|
||||||
@ -115,13 +115,7 @@ final class TestSuiteLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! $testCaseFound) {
|
if (! $testCaseFound) {
|
||||||
foreach (self::$loadedClasses as $loadedClass) {
|
return $this->exceptionFor($suiteClassName, $suiteClassFile);
|
||||||
if (is_subclass_of($loadedClass, TestCase::class)) {
|
|
||||||
$suiteClassName = $loadedClass;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! class_exists($suiteClassName, false)) {
|
if (! class_exists($suiteClassName, false)) {
|
||||||
|
|||||||
@ -13,7 +13,6 @@ trait Retrievable
|
|||||||
* @template TRetrievableValue
|
* @template TRetrievableValue
|
||||||
*
|
*
|
||||||
* 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
|
* @template TRetrievableValue
|
||||||
*
|
*
|
||||||
* @param array<string, TRetrievableValue>|object $value
|
* @param array<string, TRetrievableValue>|object $value
|
||||||
|
|||||||
26
src/Exceptions/InvalidExpectation.php
Normal file
26
src/Exceptions/InvalidExpectation.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Pest\Exceptions;
|
||||||
|
|
||||||
|
use LogicException;
|
||||||
|
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||||
|
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||||
|
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class InvalidExpectation extends LogicException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array<int, string> $methods
|
||||||
|
*
|
||||||
|
* @throws self
|
||||||
|
*/
|
||||||
|
public static function fromMethods(array $methods): never
|
||||||
|
{
|
||||||
|
throw new self(sprintf('Expectation [%s] is not valid.', implode('->', $methods)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,11 @@ namespace Pest;
|
|||||||
|
|
||||||
use BadMethodCallException;
|
use BadMethodCallException;
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Pest\Arch\Contracts\ArchExpectation;
|
||||||
|
use Pest\Arch\Expectations\ToDependOn;
|
||||||
|
use Pest\Arch\Expectations\ToDependOnNothing;
|
||||||
|
use Pest\Arch\Expectations\ToOnlyBeUsedOn;
|
||||||
|
use Pest\Arch\Expectations\ToOnlyDependOn;
|
||||||
use Pest\Concerns\Extendable;
|
use Pest\Concerns\Extendable;
|
||||||
use Pest\Concerns\Pipeable;
|
use Pest\Concerns\Pipeable;
|
||||||
use Pest\Concerns\Retrievable;
|
use Pest\Concerns\Retrievable;
|
||||||
@ -24,7 +29,7 @@ use PHPUnit\Framework\ExpectationFailedException;
|
|||||||
*
|
*
|
||||||
* @template TValue
|
* @template TValue
|
||||||
*
|
*
|
||||||
* @property Expectation $not Creates the opposite expectation.
|
* @property OppositeExpectation $not Creates the opposite expectation.
|
||||||
* @property EachExpectation $each Creates an expectation on each element on the traversable value.
|
* @property EachExpectation $each Creates an expectation on each element on the traversable value.
|
||||||
*
|
*
|
||||||
* @mixin Mixins\Expectation<TValue>
|
* @mixin Mixins\Expectation<TValue>
|
||||||
@ -350,4 +355,42 @@ final class Expectation
|
|||||||
{
|
{
|
||||||
return new Any();
|
return new Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given expectation target depends on the given dependencies.
|
||||||
|
*
|
||||||
|
* @param array<int, string>|string $dependencies
|
||||||
|
*/
|
||||||
|
public function toDependOn(array|string $dependencies): ArchExpectation
|
||||||
|
{
|
||||||
|
return ToDependOn::make($this, $dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given expectation target does not have any dependencies.
|
||||||
|
*/
|
||||||
|
public function toDependOnNothing(): ArchExpectation
|
||||||
|
{
|
||||||
|
return ToDependOnNothing::make($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given expectation dependency is only depended on by the given targets.
|
||||||
|
*
|
||||||
|
* @param array<int, string>|string $targets
|
||||||
|
*/
|
||||||
|
public function toOnlyBeUsedOn(array|string $targets): ArchExpectation
|
||||||
|
{
|
||||||
|
return ToOnlyBeUsedOn::make($this, $targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given expectation target does "only" depend on the given dependencies.
|
||||||
|
*
|
||||||
|
* @param array<int, string>|string $targets
|
||||||
|
*/
|
||||||
|
public function toOnlyDependOn(array|string $targets): ArchExpectation
|
||||||
|
{
|
||||||
|
return ToOnlyDependOn::make($this, $targets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Pest\Expectations;
|
namespace Pest\Expectations;
|
||||||
|
|
||||||
|
use Pest\Arch\Contracts\ArchExpectation;
|
||||||
|
use Pest\Arch\Expectations\ToDependOn;
|
||||||
|
use Pest\Arch\GroupArchExpectation;
|
||||||
|
use Pest\Arch\SingleArchExpectation;
|
||||||
|
use Pest\Exceptions\InvalidExpectation;
|
||||||
use Pest\Expectation;
|
use Pest\Expectation;
|
||||||
use Pest\Support\Arr;
|
use Pest\Support\Arr;
|
||||||
use PHPUnit\Framework\ExpectationFailedException;
|
use PHPUnit\Framework\ExpectationFailedException;
|
||||||
@ -52,6 +57,46 @@ final class OppositeExpectation
|
|||||||
return $this->original;
|
return $this->original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given expectation target depends on the given dependencies.
|
||||||
|
*
|
||||||
|
* @param array<int, string>|string $dependencies
|
||||||
|
*/
|
||||||
|
public function toDependOn(array|string $dependencies): ArchExpectation
|
||||||
|
{
|
||||||
|
return GroupArchExpectation::fromExpectations(array_map(fn (string $target): SingleArchExpectation => ToDependOn::make($this->original, $target)->opposite(
|
||||||
|
fn () => $this->throwExpectationFailedException('toDependOn', $target),
|
||||||
|
), is_string($dependencies) ? [$dependencies] : $dependencies));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given expectation dependency is only depended on by the given targets.
|
||||||
|
*
|
||||||
|
* @param array<int, string>|string $targets
|
||||||
|
*/
|
||||||
|
public function toOnlyBeUsedOn(array|string $targets): never
|
||||||
|
{
|
||||||
|
throw InvalidExpectation::fromMethods(['not', 'toOnlyBeUsedOn']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given expectation target does "only" depend on the given dependencies.
|
||||||
|
*
|
||||||
|
* @param array<int, string>|string $dependencies
|
||||||
|
*/
|
||||||
|
public function toOnlyDependOn(array|string $dependencies): never
|
||||||
|
{
|
||||||
|
throw InvalidExpectation::fromMethods(['not', 'toOnlyDependOn']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the given expectation target does not have any dependencies.
|
||||||
|
*/
|
||||||
|
public function toDependOnNothing(): never
|
||||||
|
{
|
||||||
|
throw InvalidExpectation::fromMethods(['not', 'toDependOnNothing']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle dynamic method calls into the original expectation.
|
* Handle dynamic method calls into the original expectation.
|
||||||
*
|
*
|
||||||
@ -89,10 +134,12 @@ final class OppositeExpectation
|
|||||||
/**
|
/**
|
||||||
* Creates a new expectation failed exception with a nice readable message.
|
* Creates a new expectation failed exception with a nice readable message.
|
||||||
*
|
*
|
||||||
* @param array<int, mixed> $arguments
|
* @param array<int, mixed>|string $arguments
|
||||||
*/
|
*/
|
||||||
private function throwExpectationFailedException(string $name, array $arguments = []): never
|
public function throwExpectationFailedException(string $name, array|string $arguments = []): never
|
||||||
{
|
{
|
||||||
|
$arguments = is_array($arguments) ? $arguments : [$arguments];
|
||||||
|
|
||||||
$exporter = new Exporter();
|
$exporter = new Exporter();
|
||||||
|
|
||||||
$toString = fn ($argument): string => $exporter->shortenedExport($argument);
|
$toString = fn ($argument): string => $exporter->shortenedExport($argument);
|
||||||
|
|||||||
@ -46,7 +46,7 @@ final class Memory implements AddsOutput, HandlesArguments
|
|||||||
{
|
{
|
||||||
if ($this->enabled) {
|
if ($this->enabled) {
|
||||||
$this->output->writeln(sprintf(
|
$this->output->writeln(sprintf(
|
||||||
' <fg=gray;options=bold>Memory:</> <fg=default>%s MB</>',
|
' <fg=gray>Memory:</> <fg=default>%s MB</>',
|
||||||
round(memory_get_usage(true) / 1000 ** 2, 3)
|
round(memory_get_usage(true) / 1000 ** 2, 3)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -162,11 +162,10 @@ final class DatasetsRepository
|
|||||||
$datasets[$index] = iterator_to_array($datasets[$index], $preserveKeysForArrayIterator);
|
$datasets[$index] = iterator_to_array($datasets[$index], $preserveKeysForArrayIterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
foreach ($datasets[$index] as $key => $values) {
|
foreach ($datasets[$index] as $key => $values) {
|
||||||
$values = is_array($values) ? $values : [$values];
|
$values = is_array($values) ? $values : [$values];
|
||||||
$processedDataset[] = [
|
$processedDataset[] = [
|
||||||
'label' => self::getDatasetDescription($key, $values), // @phpstan-ignore-line
|
'label' => self::getDatasetDescription($key, $values),
|
||||||
'values' => $values,
|
'values' => $values,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -781,6 +781,9 @@
|
|||||||
PASS Tests\PHPUnit\CustomAffixes\snakecasespec
|
PASS Tests\PHPUnit\CustomAffixes\snakecasespec
|
||||||
✓ it runs file names like `snake_case_spec.php`
|
✓ it runs file names like `snake_case_spec.php`
|
||||||
|
|
||||||
|
PASS Tests\CustomTestCase\ExecutedTest
|
||||||
|
✓ that gets executed
|
||||||
|
|
||||||
PASS Tests\PHPUnit\CustomTestCase\UsesPerDirectory
|
PASS Tests\PHPUnit\CustomTestCase\UsesPerDirectory
|
||||||
✓ closure was bound to CustomTestCase
|
✓ closure was bound to CustomTestCase
|
||||||
|
|
||||||
@ -817,6 +820,10 @@
|
|||||||
✓ it show the actual dataset of multiple non-named datasets in their description
|
✓ it show the actual dataset of multiple non-named datasets in their description
|
||||||
✓ it show the correct description for mixed named and not-named datasets
|
✓ it show the correct description for mixed named and not-named datasets
|
||||||
|
|
||||||
|
PASS Tests\Unit\Expectations\OppositeExpectation
|
||||||
|
✓ it throw expectation failed exception with string argument
|
||||||
|
✓ it throw expectation failed exception with array argument
|
||||||
|
|
||||||
PASS Tests\Unit\Plugins\Environment
|
PASS Tests\Unit\Plugins\Environment
|
||||||
✓ environment is set to CI when --ci option is used
|
✓ environment is set to CI when --ci option is used
|
||||||
✓ environment is set to Local when --ci option is not used
|
✓ environment is set to Local when --ci option is not used
|
||||||
@ -892,4 +899,4 @@
|
|||||||
PASS Tests\Visual\Version
|
PASS Tests\Visual\Version
|
||||||
✓ visual snapshot of help command output
|
✓ visual snapshot of help command output
|
||||||
|
|
||||||
Tests: 4 incomplete, 2 todos, 18 skipped, 621 passed (1531 assertions)
|
|
||||||
|
|||||||
16
tests/Unit/Expectations/OppositeExpectation.php
Normal file
16
tests/Unit/Expectations/OppositeExpectation.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Pest\Expectations\OppositeExpectation;
|
||||||
|
use PHPUnit\Framework\ExpectationFailedException;
|
||||||
|
|
||||||
|
it('throw expectation failed exception with string argument', function (): void {
|
||||||
|
$expectation = new OppositeExpectation(expect('foo'));
|
||||||
|
|
||||||
|
$expectation->throwExpectationFailedException('toBe', 'bar');
|
||||||
|
})->throws(ExpectationFailedException::class, "Expecting 'foo' not to be 'bar'.");
|
||||||
|
|
||||||
|
it('throw expectation failed exception with array argument', function (): void {
|
||||||
|
$expectation = new OppositeExpectation(expect('foo'));
|
||||||
|
|
||||||
|
$expectation->throwExpectationFailedException('toBe', ['bar']);
|
||||||
|
})->throws(ExpectationFailedException::class, "Expecting 'foo' not to be 'bar'.");
|
||||||
Reference in New Issue
Block a user