mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 07:47:22 +01:00
Merge pull request #895 from cerbero90/feature/traversable-sequence
[2.x] Add support for nested traversable in sequence
This commit is contained in:
@ -6,6 +6,8 @@ namespace Pest;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use OutOfRangeException;
|
||||
use Pest\Arch\Contracts\ArchExpectation;
|
||||
use Pest\Arch\Expectations\Targeted;
|
||||
use Pest\Arch\Expectations\ToBeUsedIn;
|
||||
@ -28,7 +30,6 @@ use Pest\Expectations\OppositeExpectation;
|
||||
use Pest\Matchers\Any;
|
||||
use Pest\Support\ExpectationPipeline;
|
||||
use PHPUnit\Architecture\Elements\ObjectDescription;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
/**
|
||||
@ -219,30 +220,26 @@ final class Expectation
|
||||
throw new BadMethodCallException('Expectation value is not iterable.');
|
||||
}
|
||||
|
||||
$value = is_array($this->value) ? $this->value : iterator_to_array($this->value);
|
||||
$keys = array_keys($value);
|
||||
$values = array_values($value);
|
||||
$callbacksCount = count($callbacks);
|
||||
|
||||
$index = 0;
|
||||
|
||||
while (count($callbacks) < count($values)) {
|
||||
$callbacks[] = $callbacks[$index];
|
||||
$index = $index < count($values) - 1 ? $index + 1 : 0;
|
||||
if (count($callbacks) == 0) {
|
||||
throw new InvalidArgumentException('No sequence expectations defined.');
|
||||
}
|
||||
|
||||
if ($callbacksCount > count($values)) {
|
||||
Assert::assertLessThanOrEqual(count($value), count($callbacks));
|
||||
}
|
||||
$index = $valuesCount = 0;
|
||||
|
||||
foreach ($values as $key => $item) {
|
||||
if ($callbacks[$key] instanceof Closure) {
|
||||
call_user_func($callbacks[$key], new self($item), new self($keys[$key]));
|
||||
foreach ($this->value as $key => $value) {
|
||||
$valuesCount++;
|
||||
|
||||
continue;
|
||||
if ($callbacks[$index] instanceof Closure) {
|
||||
$callbacks[$index](new self($value), new self($key));
|
||||
} else {
|
||||
(new self($value))->toEqual($callbacks[$index]);
|
||||
}
|
||||
|
||||
(new self($item))->toEqual($callbacks[$key]);
|
||||
$index = isset($callbacks[$index + 1]) ? $index + 1 : 0;
|
||||
}
|
||||
|
||||
if (count($callbacks) > $valuesCount) {
|
||||
throw new OutOfRangeException('Sequence expectations are more than the iterable items.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
@ -297,12 +297,14 @@
|
||||
|
||||
PASS Tests\Features\Expect\sequence
|
||||
✓ an exception is thrown if the the type is not iterable
|
||||
✓ an exception is thrown if there are no expectations
|
||||
✓ allows for sequences of checks to be run on iterable data
|
||||
✓ loops back to the start if it runs out of sequence items
|
||||
✓ fails if the number of iterable items is greater than the number of expectations
|
||||
✓ fails if the number of iterable items is less than the number of expectations
|
||||
✓ it works with associative arrays
|
||||
✓ it can be passed non-callable values
|
||||
✓ it can be passed a mixture of value types
|
||||
✓ it works with traversables
|
||||
|
||||
PASS Tests\Features\Expect\toBe
|
||||
✓ strict comparisons
|
||||
@ -1246,4 +1248,4 @@
|
||||
WARN Tests\Visual\Version
|
||||
- visual snapshot of help command output
|
||||
|
||||
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 19 skipped, 882 passed (2022 assertions)
|
||||
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 19 skipped, 882 passed (2022 assertions)
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('an exception is thrown if the the type is not iterable', function () {
|
||||
expect('Foobar')->each->sequence();
|
||||
})->throws(BadMethodCallException::class, 'Expectation value is not iterable.');
|
||||
|
||||
test('an exception is thrown if there are no expectations', function () {
|
||||
expect(['Foobar'])->sequence();
|
||||
})->throws(InvalidArgumentException::class, 'No sequence expectations defined.');
|
||||
|
||||
test('allows for sequences of checks to be run on iterable data', function () {
|
||||
expect([1, 2, 3])
|
||||
->sequence(
|
||||
@ -40,7 +42,7 @@ test('loops back to the start if it runs out of sequence items', function () {
|
||||
expect(static::getCount())->toBe(16);
|
||||
});
|
||||
|
||||
test('fails if the number of iterable items is greater than the number of expectations', function () {
|
||||
test('fails if the number of iterable items is less than the number of expectations', function () {
|
||||
expect([1, 2])
|
||||
->sequence(
|
||||
function ($expectation) {
|
||||
@ -53,7 +55,7 @@ test('fails if the number of iterable items is greater than the number of expect
|
||||
$expectation->toBeInt()->toEqual(3);
|
||||
},
|
||||
);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
})->throws(OutOfRangeException::class, 'Sequence expectations are more than the iterable items.');
|
||||
|
||||
test('it works with associative arrays', function () {
|
||||
expect(['foo' => 'bar', 'baz' => 'boom'])
|
||||
@ -86,3 +88,26 @@ test('it can be passed a mixture of value types', function () {
|
||||
|
||||
expect(static::getCount())->toBe(4);
|
||||
});
|
||||
|
||||
test('it works with traversables', function () {
|
||||
$generator = (function () {
|
||||
yield 'one' => (fn () => yield from [1, 2, 3])();
|
||||
yield 'two' => (fn () => yield from [4, 5, 6])();
|
||||
yield 'three' => (fn () => yield from [7, 8, 9])();
|
||||
})();
|
||||
|
||||
expect($generator)->sequence(
|
||||
fn ($value, $key) => $key->toBe('one')
|
||||
->and($value)
|
||||
->toBeInstanceOf(Generator::class)
|
||||
->sequence(1, 2, 3),
|
||||
fn ($value, $key) => $key->toBe('two')
|
||||
->and($value)
|
||||
->toBeInstanceOf(Generator::class)
|
||||
->sequence(4, 5, 6),
|
||||
fn ($value, $key) => $key->toBe('three')
|
||||
->and($value)
|
||||
->toBeInstanceOf(Generator::class)
|
||||
->sequence(7, 8, 9),
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user