Merge branch '2.x' into 2.x-fix-ignored-description-for-lazy-datasets

This commit is contained in:
Nuno Maduro
2022-12-23 23:01:39 +00:00
committed by GitHub
11 changed files with 153 additions and 21 deletions

View File

@ -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>

View File

@ -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",

View File

@ -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)) {

View File

@ -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

View 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)));
}
}

View File

@ -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);
}
} }

View File

@ -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);

View File

@ -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)
)); ));
} }

View File

@ -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,
]; ];
} }

View File

@ -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)

View 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'.");