mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
Merge remote-tracking branch 'origin/master' into add-new-ci-option-to-pest-bin
# Conflicts: # tests/.snapshots/success.txt
This commit is contained in:
@ -157,6 +157,7 @@ final class Expectation
|
||||
$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;
|
||||
|
||||
@ -165,6 +166,10 @@ final class Expectation
|
||||
$index = $index < count($values) - 1 ? $index + 1 : 0;
|
||||
}
|
||||
|
||||
if ($callbacksCount > count($values)) {
|
||||
Assert::assertLessThanOrEqual(count($value), count($callbacks));
|
||||
}
|
||||
|
||||
foreach ($values as $key => $item) {
|
||||
if (is_callable($callbacks[$key])) {
|
||||
call_user_func($callbacks[$key], new self($item), new self($keys[$key]));
|
||||
@ -177,6 +182,88 @@ final class Expectation
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the subject matches one of the given "expressions", the expression callback will run.
|
||||
*
|
||||
* @template TMatchSubject of array-key
|
||||
*
|
||||
* @param callable(): TMatchSubject|TMatchSubject $subject
|
||||
* @param array<TMatchSubject, (callable(Expectation<TValue>): mixed)|TValue> $expressions
|
||||
*/
|
||||
public function match($subject, array $expressions): Expectation
|
||||
{
|
||||
$subject = is_callable($subject)
|
||||
? $subject
|
||||
: function () use ($subject) {
|
||||
return $subject;
|
||||
};
|
||||
|
||||
$subject = $subject();
|
||||
|
||||
$matched = false;
|
||||
|
||||
foreach ($expressions as $key => $callback) {
|
||||
if ($subject != $key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$matched = true;
|
||||
|
||||
if (is_callable($callback)) {
|
||||
$callback(new self($this->value));
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->and($this->value)->toEqual($callback);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($matched === false) {
|
||||
throw new ExpectationFailedException('Unhandled match value.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the callback if the given "condition" is falsy.
|
||||
*
|
||||
* @param (callable(): bool)|bool $condition
|
||||
* @param callable(Expectation<TValue>): mixed $callback
|
||||
*/
|
||||
public function unless($condition, callable $callback): Expectation
|
||||
{
|
||||
$condition = is_callable($condition)
|
||||
? $condition
|
||||
: static function () use ($condition): mixed {
|
||||
return $condition;
|
||||
};
|
||||
|
||||
return $this->when(!$condition(), $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the callback if the given "condition" is truthy.
|
||||
*
|
||||
* @param (callable(): bool)|bool $condition
|
||||
* @param callable(Expectation<TValue>): mixed $callback
|
||||
*/
|
||||
public function when($condition, callable $callback): Expectation
|
||||
{
|
||||
$condition = is_callable($condition)
|
||||
? $condition
|
||||
: static function () use ($condition): mixed {
|
||||
return $condition;
|
||||
};
|
||||
|
||||
if ($condition()) {
|
||||
$callback($this->and($this->value));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that two variables have the same type and
|
||||
* value. Used on objects, it asserts that two
|
||||
|
||||
@ -78,6 +78,26 @@ final class TestCall
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the test throws the given `$exceptionClass` when called if the given condition is true.
|
||||
*
|
||||
* @param (callable(): bool)|bool $condition
|
||||
*/
|
||||
public function throwsIf($condition, string $exception, string $exceptionMessage = null): TestCall
|
||||
{
|
||||
$condition = is_callable($condition)
|
||||
? $condition
|
||||
: static function () use ($condition): mixed {
|
||||
return $condition;
|
||||
};
|
||||
|
||||
if ($condition()) {
|
||||
return $this->throws($exception, $exceptionMessage);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the current test multiple times with
|
||||
* each item of the given `iterable`.
|
||||
|
||||
@ -107,6 +107,11 @@
|
||||
✓ it catch exceptions
|
||||
✓ it catch exceptions and messages
|
||||
✓ it can just define the message
|
||||
✓ it not catch exceptions if given condition is false
|
||||
✓ it catch exceptions if given condition is true
|
||||
✓ it catch exceptions and messages if given condition is true
|
||||
✓ it can just define the message if given condition is true
|
||||
✓ it can just define the message if given condition is 1
|
||||
|
||||
PASS Tests\Features\Expect\HigherOrder\methods
|
||||
✓ it can access methods
|
||||
@ -158,6 +163,17 @@
|
||||
✓ it properly parses json string
|
||||
✓ fails with broken json string
|
||||
|
||||
PASS Tests\Features\Expect\matchExpectation
|
||||
✓ it pass
|
||||
✓ it failures
|
||||
✓ it runs with truthy
|
||||
✓ it runs with falsy
|
||||
✓ it runs with truthy closure condition
|
||||
✓ it runs with falsy closure condition
|
||||
✓ it can be passed non-callable values
|
||||
✓ it fails with unhandled match
|
||||
✓ it can be used in higher order tests
|
||||
|
||||
PASS Tests\Features\Expect\not
|
||||
✓ not property calls
|
||||
|
||||
@ -168,7 +184,7 @@
|
||||
✓ an exception is thrown if the the type is not iterable
|
||||
✓ allows for sequences of checks to be run on iterable data
|
||||
✓ loops back to the start if it runs out of sequence items
|
||||
✓ it works if the number of items in the iterable is smaller than the number of expectations
|
||||
✓ fails if the number of iterable items is greater 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
|
||||
@ -481,6 +497,24 @@
|
||||
✓ closure missing parameter
|
||||
✓ closure missing type-hint
|
||||
|
||||
PASS Tests\Features\Expect\unless
|
||||
✓ it pass
|
||||
✓ it failures
|
||||
✓ it runs with truthy
|
||||
✓ it skips with falsy
|
||||
✓ it runs with truthy closure condition
|
||||
✓ it skips with falsy closure condition
|
||||
✓ it can be used in higher order tests
|
||||
|
||||
PASS Tests\Features\Expect\when
|
||||
✓ it pass
|
||||
✓ it failures
|
||||
✓ it runs with truthy
|
||||
✓ it skips with falsy
|
||||
✓ it runs with truthy closure condition
|
||||
✓ it skips with falsy closure condition
|
||||
✓ it can be used in higher order tests
|
||||
|
||||
PASS Tests\Features\Helpers
|
||||
✓ it can set/get properties on $this
|
||||
✓ it throws error if property do not exist
|
||||
@ -623,10 +657,6 @@
|
||||
✓ 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
|
||||
|
||||
PASS Tests\Unit\Plugins\Context
|
||||
✓ environment is set to CI when --ci option is used
|
||||
✓ environment is set to Local when --ci option is not used
|
||||
|
||||
PASS Tests\Unit\Plugins\Version
|
||||
✓ it outputs the version when --version is used
|
||||
✓ it do not outputs version when --version is not used
|
||||
@ -652,7 +682,6 @@
|
||||
✓ it alerts users about tests with arguments but no input
|
||||
✓ it can return an array of all test suite filenames
|
||||
✓ it can filter the test suite filenames to those with the only method
|
||||
✓ it does not filter the test suite filenames to those with the only method when working in CI pipeline
|
||||
|
||||
PASS Tests\Visual\Help
|
||||
✓ visual snapshot of help command output
|
||||
@ -686,5 +715,5 @@
|
||||
✓ it is a test
|
||||
✓ it uses correct parent class
|
||||
|
||||
Tests: 4 incompleted, 9 skipped, 450 passed
|
||||
Tests: 4 incompleted, 9 skipped, 475 passed
|
||||
|
||||
@ -17,3 +17,23 @@ it('catch exceptions and messages', function () {
|
||||
it('can just define the message', function () {
|
||||
throw new Exception('Something bad happened');
|
||||
})->throws('Something bad happened');
|
||||
|
||||
it('not catch exceptions if given condition is false', function () {
|
||||
$this->assertTrue(true);
|
||||
})->throwsIf(false, Exception::class);
|
||||
|
||||
it('catch exceptions if given condition is true', function () {
|
||||
throw new Exception('Something bad happened');
|
||||
})->throwsIf(function () { return true; }, Exception::class);
|
||||
|
||||
it('catch exceptions and messages if given condition is true', function () {
|
||||
throw new Exception('Something bad happened');
|
||||
})->throwsIf(true, Exception::class, 'Something bad happened');
|
||||
|
||||
it('can just define the message if given condition is true', function () {
|
||||
throw new Exception('Something bad happened');
|
||||
})->throwsIf(true, 'Something bad happened');
|
||||
|
||||
it('can just define the message if given condition is 1', function () {
|
||||
throw new Exception('Something bad happened');
|
||||
})->throwsIf(1, 'Something bad happened');
|
||||
|
||||
148
tests/Features/Expect/matchExpectation.php
Normal file
148
tests/Features/Expect/matchExpectation.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->matched = null;
|
||||
});
|
||||
|
||||
it('pass', function () {
|
||||
expect('baz')
|
||||
->match('foo', [
|
||||
'bar' => function ($value) {
|
||||
$this->matched = 'bar';
|
||||
|
||||
return $value->toEqual('bar');
|
||||
},
|
||||
'foo' => function ($value) {
|
||||
$this->matched = 'baz';
|
||||
|
||||
return $value->toEqual('baz');
|
||||
},
|
||||
]
|
||||
)
|
||||
->toEqual($this->matched);
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('failures', function () {
|
||||
expect(true)
|
||||
->match('foo', [
|
||||
'bar' => function ($value) {
|
||||
return $value->toBeTrue();
|
||||
},
|
||||
'foo' => function ($value) {
|
||||
return $value->toBeFalse();
|
||||
},
|
||||
]
|
||||
);
|
||||
})->throws(ExpectationFailedException::class, 'true is false');
|
||||
|
||||
it('runs with truthy', function () {
|
||||
expect('foo')
|
||||
->match(1, [
|
||||
'bar' => function ($value) {
|
||||
$this->matched = 'bar';
|
||||
|
||||
return $value->toEqual('bar');
|
||||
},
|
||||
true => function ($value) {
|
||||
$this->matched = 'foo';
|
||||
|
||||
return $value->toEqual('foo');
|
||||
},
|
||||
]
|
||||
)
|
||||
->toEqual($this->matched);
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('runs with falsy', function () {
|
||||
expect('foo')
|
||||
->match(false, [
|
||||
'bar' => function ($value) {
|
||||
$this->matched = 'bar';
|
||||
|
||||
return $value->toEqual('bar');
|
||||
},
|
||||
false => function ($value) {
|
||||
$this->matched = 'foo';
|
||||
|
||||
return $value->toEqual('foo');
|
||||
},
|
||||
]
|
||||
)
|
||||
->toEqual($this->matched);
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('runs with truthy closure condition', function () {
|
||||
expect('foo')
|
||||
->match(
|
||||
function () { return '1'; }, [
|
||||
'bar' => function ($value) {
|
||||
$this->matched = 'bar';
|
||||
|
||||
return $value->toEqual('bar');
|
||||
},
|
||||
true => function ($value) {
|
||||
$this->matched = 'foo';
|
||||
|
||||
return $value->toEqual('foo');
|
||||
},
|
||||
]
|
||||
)
|
||||
->toEqual($this->matched);
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('runs with falsy closure condition', function () {
|
||||
expect('foo')
|
||||
->match(
|
||||
function () { return '0'; }, [
|
||||
'bar' => function ($value) {
|
||||
$this->matched = 'bar';
|
||||
|
||||
return $value->toEqual('bar');
|
||||
},
|
||||
false => function ($value) {
|
||||
$this->matched = 'foo';
|
||||
|
||||
return $value->toEqual('foo');
|
||||
},
|
||||
]
|
||||
)
|
||||
->toEqual($this->matched);
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('can be passed non-callable values', function () {
|
||||
expect('foo')
|
||||
->match('pest', [
|
||||
'bar' => 'foo',
|
||||
'pest' => 'baz',
|
||||
]
|
||||
);
|
||||
})->throws(ExpectationFailedException::class, 'two strings are equal');
|
||||
|
||||
it('fails with unhandled match', function () {
|
||||
expect('foo')->match('bar', []);
|
||||
})->throws(ExpectationFailedException::class, 'Unhandled match value.');
|
||||
|
||||
it('can be used in higher order tests')
|
||||
->expect(true)
|
||||
->match(
|
||||
function () { return true; }, [
|
||||
false => function ($value) {
|
||||
return $value->toBeFalse();
|
||||
},
|
||||
true => function ($value) {
|
||||
return $value->toBeTrue();
|
||||
},
|
||||
]
|
||||
);
|
||||
@ -1,5 +1,7 @@
|
||||
<?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.');
|
||||
@ -26,16 +28,14 @@ test('loops back to the start if it runs out of sequence items', function () {
|
||||
expect(static::getCount())->toBe(16);
|
||||
});
|
||||
|
||||
test('it works if the number of items in the iterable is smaller than the number of expectations', function () {
|
||||
test('fails if the number of iterable items is greater than the number of expectations', function () {
|
||||
expect([1, 2])
|
||||
->sequence(
|
||||
function ($expectation) { $expectation->toBeInt()->toEqual(1); },
|
||||
function ($expectation) { $expectation->toBeInt()->toEqual(2); },
|
||||
function ($expectation) { $expectation->toBeInt()->toEqual(3); },
|
||||
);
|
||||
|
||||
expect(static::getCount())->toBe(4);
|
||||
});
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('it works with associative arrays', function () {
|
||||
expect(['foo' => 'bar', 'baz' => 'boom'])
|
||||
|
||||
101
tests/Features/Expect/unless.php
Normal file
101
tests/Features/Expect/unless.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->unlessObject = new stdClass();
|
||||
$this->unlessObject->trueValue = true;
|
||||
$this->unlessObject->foo = 'foo';
|
||||
});
|
||||
|
||||
it('pass', function () {
|
||||
expect('foo')
|
||||
->unless(
|
||||
true,
|
||||
function ($value) {
|
||||
return $value->toEqual('bar');
|
||||
}
|
||||
)
|
||||
->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(1);
|
||||
});
|
||||
|
||||
it('failures', function () {
|
||||
expect('foo')
|
||||
->unless(
|
||||
false,
|
||||
function ($value) {
|
||||
return $value->toBeTrue();
|
||||
}
|
||||
)
|
||||
->toEqual('foo');
|
||||
})->throws(ExpectationFailedException::class, 'is true');
|
||||
|
||||
it('runs with truthy', function () {
|
||||
expect($this->unlessObject)
|
||||
->unless(
|
||||
0,
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeTrue();
|
||||
}
|
||||
)
|
||||
->foo->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('skips with falsy', function () {
|
||||
expect($this->unlessObject)
|
||||
->unless(
|
||||
1,
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeFalse(); // fails
|
||||
}
|
||||
)
|
||||
->unless(
|
||||
true,
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeFalse(); // fails
|
||||
}
|
||||
)
|
||||
->foo->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(1);
|
||||
});
|
||||
|
||||
it('runs with truthy closure condition', function () {
|
||||
expect($this->unlessObject)
|
||||
->unless(
|
||||
function () { return '0'; },
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeTrue();
|
||||
}
|
||||
)
|
||||
->foo->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('skips with falsy closure condition', function () {
|
||||
expect($this->unlessObject)
|
||||
->unless(
|
||||
function () { return '1'; },
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeFalse(); // fails
|
||||
}
|
||||
)
|
||||
->foo->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(1);
|
||||
});
|
||||
|
||||
it('can be used in higher order tests')
|
||||
->expect(true)
|
||||
->unless(
|
||||
function () { return false; },
|
||||
function ($value) {
|
||||
return $value->toBeFalse();
|
||||
}
|
||||
)
|
||||
->throws(ExpectationFailedException::class, 'true is false');
|
||||
101
tests/Features/Expect/when.php
Normal file
101
tests/Features/Expect/when.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->whenObject = new stdClass();
|
||||
$this->whenObject->trueValue = true;
|
||||
$this->whenObject->foo = 'foo';
|
||||
});
|
||||
|
||||
it('pass', function () {
|
||||
expect('foo')
|
||||
->when(
|
||||
true,
|
||||
function ($value) {
|
||||
return $value->toEqual('foo');
|
||||
}
|
||||
)
|
||||
->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('failures', function () {
|
||||
expect('foo')
|
||||
->when(
|
||||
true,
|
||||
function ($value) {
|
||||
return $value->toBeTrue();
|
||||
}
|
||||
)
|
||||
->toEqual('foo');
|
||||
})->throws(ExpectationFailedException::class, 'is true');
|
||||
|
||||
it('runs with truthy', function () {
|
||||
expect($this->whenObject)
|
||||
->when(
|
||||
1,
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeTrue();
|
||||
}
|
||||
)
|
||||
->foo->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('skips with falsy', function () {
|
||||
expect($this->whenObject)
|
||||
->when(
|
||||
0,
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeFalse(); // fails
|
||||
}
|
||||
)
|
||||
->when(
|
||||
false,
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeFalse(); // fails
|
||||
}
|
||||
)
|
||||
->foo->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(1);
|
||||
});
|
||||
|
||||
it('runs with truthy closure condition', function () {
|
||||
expect($this->whenObject)
|
||||
->when(
|
||||
function () { return '1'; },
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeTrue();
|
||||
}
|
||||
)
|
||||
->foo->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(2);
|
||||
});
|
||||
|
||||
it('skips with falsy closure condition', function () {
|
||||
expect($this->whenObject)
|
||||
->when(
|
||||
function () { return '0'; },
|
||||
function ($value) {
|
||||
return $value->trueValue->toBeFalse(); // fails
|
||||
}
|
||||
)
|
||||
->foo->toEqual('foo');
|
||||
|
||||
expect(static::getCount())->toBe(1);
|
||||
});
|
||||
|
||||
it('can be used in higher order tests')
|
||||
->expect(false)
|
||||
->when(
|
||||
function () { return true; },
|
||||
function ($value) {
|
||||
return $value->toBeTrue();
|
||||
}
|
||||
)
|
||||
->throws(ExpectationFailedException::class, 'false is true');
|
||||
Reference in New Issue
Block a user