Merge branch 'master' into non-callable-sequence

# Conflicts:
#	tests/.snapshots/success.txt
This commit is contained in:
luke
2021-06-18 21:45:02 +01:00
9 changed files with 190 additions and 17 deletions

1
.github/FUNDING.yml vendored
View File

@ -2,4 +2,3 @@
github: [nunomaduro,owenvoke,olivernybroe,octoper]
patreon: nunomaduro
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L

View File

@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [v1.6.0 (2021-06-18)](https://github.com/pestphp/pest/compare/v1.5.0...v1.6.0)
### Added
- Adds a new `json` expectation method to improve testing with JSON strings ([#325](https://github.com/pestphp/pest/pull/325))
- Adds dot notation support to the `toHaveKey` and `toHaveKeys` expectations ([#322](https://github.com/pestphp/pest/pull/322))
## [v1.5.0 (2021-06-15)](https://github.com/pestphp/pest/compare/v1.4.0...v1.5.0)
### Changed
- Moves plugins from the `require` section to the core itself ([#317](https://github.com/pestphp/pest/pull/317)), ([#318](https://github.com/pestphp/pest/pull/318)), ([#320](https://github.com/pestphp/pest/pull/320))

View File

@ -6,8 +6,10 @@ namespace Pest;
use BadMethodCallException;
use Pest\Concerns\Extendable;
use Pest\Support\Arr;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Exporter\Exporter;
/**
@ -58,6 +60,14 @@ final class Expectation
return new self($value);
}
/**
* Creates a new expectation with the decoded JSON value.
*/
public function json(): Expectation
{
return $this->toBeJson()->and(json_decode($this->value, true));
}
/**
* Dump the expectation value and end the script.
*
@ -529,10 +539,16 @@ final class Expectation
$array = (array) $this->value;
}
Assert::assertArrayHasKey($key, $array);
try {
Assert::assertTrue(Arr::has($array, $key));
/* @phpstan-ignore-next-line */
} catch (ExpectationFailedException $exception) {
throw new ExpectationFailedException("Failed asserting that an array has the key '$key'", $exception->getComparisonFailure());
}
if (func_num_args() > 1) {
Assert::assertEquals($value, $array[$key]);
Assert::assertEquals($value, Arr::get($array, $key));
}
return $this;

View File

@ -6,7 +6,7 @@ namespace Pest;
function version(): string
{
return '1.5.0';
return '1.6.0';
}
function testDirectory(string $file = ''): string

68
src/Support/Arr.php Normal file
View File

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace Pest\Support;
/**
* Credits: most of this class methods and implementations
* belongs to the Arr helper of laravel/framework project
* (https://github.com/laravel/framework).
*
* @internal
*/
final class Arr
{
/**
* @param array<mixed> $array
* @param string|int $key
*/
public static function has(array $array, $key): bool
{
$key = (string) $key;
if (array_key_exists($key, $array)) {
return true;
}
foreach (explode('.', $key) as $segment) {
if (is_array($array) && array_key_exists($segment, $array)) {
$array = $array[$segment];
} else {
return false;
}
}
return true;
}
/**
* @param array<mixed> $array
* @param string|int $key
* @param null $default
*
* @return array|mixed|null
*/
public static function get(array $array, $key, $default = null)
{
$key = (string) $key;
if (array_key_exists($key, $array)) {
return $array[$key];
}
if (strpos($key, '.') === false) {
return $array[$key] ?? $default;
}
foreach (explode('.', $key) as $segment) {
if (is_array($array) && array_key_exists($segment, $array)) {
$array = $array[$segment];
} else {
return $default;
}
}
return $array;
}
}

View File

@ -140,6 +140,10 @@
✓ it macros true is true with argument
✓ it macros false is not true with argument
PASS Tests\Features\Expect\json
✓ it properly parses json string
✓ fails with broken json string
PASS Tests\Features\Expect\not
✓ not property calls
@ -152,8 +156,6 @@
✓ 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
✓ it works with associative arrays
✓ it can be passed non-callable values
✓ it can be passed a mixture of value types
PASS Tests\Features\Expect\toBe
✓ strict comparisons
@ -338,8 +340,23 @@
PASS Tests\Features\Expect\toHaveKey
✓ pass
✓ pass with nested key
✓ pass with plain key with dots
✓ pass with value check
✓ pass with value check and nested key
✓ pass with value check and plain key with dots
✓ failures
✓ failures with nested key
✓ failures with plain key with dots
✓ fails with wrong value
✓ fails with wrong value and nested key
✓ fails with wrong value and plain key with dots
✓ not failures
✓ not failures with nested key
✓ not failures with plain key with dots
✓ not failures with correct value
✓ not failures with correct value and with nested key
✓ not failures with correct value and with plain key with dots
PASS Tests\Features\Expect\toHaveKeys
✓ pass
@ -556,5 +573,5 @@
✓ it is a test
✓ it uses correct parent class
Tests: 4 incompleted, 7 skipped, 342 passed
Tests: 4 incompleted, 7 skipped, 357 passed

View File

@ -0,0 +1,14 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('it properly parses json string', function () {
expect('{"name":"Nuno"}')
->json()
->name
->toBe('Nuno');
});
test('fails with broken json string', function () {
expect('{":"Nuno"}')->json();
})->throws(ExpectationFailedException::class);

View File

@ -2,14 +2,68 @@
use PHPUnit\Framework\ExpectationFailedException;
test('pass', function () {
expect(['a' => 1, 'b', 'c' => 'world'])->toHaveKey('c');
});
$test_array = [
'a' => 1,
'b',
'c' => 'world',
'd' => [
'e' => 'hello',
],
'key.with.dots' => false,
];
test('failures', function () {
expect(['a' => 1, 'b', 'c' => 'world'])->toHaveKey('hello');
test('pass')->expect($test_array)->toHaveKey('c');
test('pass with nested key')->expect($test_array)->toHaveKey('d.e');
test('pass with plain key with dots')->expect($test_array)->toHaveKey('key.with.dots');
test('pass with value check')->expect($test_array)->toHaveKey('c', 'world');
test('pass with value check and nested key')->expect($test_array)->toHaveKey('d.e', 'hello');
test('pass with value check and plain key with dots')->expect($test_array)->toHaveKey('key.with.dots', false);
test('failures', function () use ($test_array) {
expect($test_array)->toHaveKey('foo');
})->throws(ExpectationFailedException::class, "Failed asserting that an array has the key 'foo'");
test('failures with nested key', function () use ($test_array) {
expect($test_array)->toHaveKey('d.bar');
})->throws(ExpectationFailedException::class, "Failed asserting that an array has the key 'd.bar'");
test('failures with plain key with dots', function () use ($test_array) {
expect($test_array)->toHaveKey('missing.key.with.dots');
})->throws(ExpectationFailedException::class, "Failed asserting that an array has the key 'missing.key.with.dots'");
test('fails with wrong value', function () use ($test_array) {
expect($test_array)->toHaveKey('c', 'bar');
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect(['a' => 1, 'hello' => 'world', 'c'])->not->toHaveKey('hello');
test('fails with wrong value and nested key', function () use ($test_array) {
expect($test_array)->toHaveKey('d.e', 'foo');
})->throws(ExpectationFailedException::class);
test('fails with wrong value and plain key with dots', function () use ($test_array) {
expect($test_array)->toHaveKey('key.with.dots', true);
})->throws(ExpectationFailedException::class);
test('not failures', function () use ($test_array) {
expect($test_array)->not->toHaveKey('c');
})->throws(ExpectationFailedException::class, "Expecting Array (...) not to have key 'c'");
test('not failures with nested key', function () use ($test_array) {
expect($test_array)->not->toHaveKey('d.e');
})->throws(ExpectationFailedException::class, "Expecting Array (...) not to have key 'd.e'");
test('not failures with plain key with dots', function () use ($test_array) {
expect($test_array)->not->toHaveKey('key.with.dots');
})->throws(ExpectationFailedException::class, "Expecting Array (...) not to have key 'key.with.dots'");
test('not failures with correct value', function () use ($test_array) {
expect($test_array)->not->toHaveKey('c', 'world');
})->throws(ExpectationFailedException::class);
test('not failures with correct value and with nested key', function () use ($test_array) {
expect($test_array)->not->toHaveKey('d.e', 'hello');
})->throws(ExpectationFailedException::class);
test('not failures with correct value and with plain key with dots', function () use ($test_array) {
expect($test_array)->not->toHaveKey('key.with.dots', false);
})->throws(ExpectationFailedException::class);

View File

@ -3,13 +3,13 @@
use PHPUnit\Framework\ExpectationFailedException;
test('pass', function () {
expect(['a' => 1, 'b', 'c' => 'world'])->toHaveKeys(['a', 'c']);
expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => 'baz']])->toHaveKeys(['a', 'c', 'foo.bar']);
});
test('failures', function () {
expect(['a' => 1, 'b', 'c' => 'world'])->toHaveKeys(['a', 'd']);
expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => 'baz']])->toHaveKeys(['a', 'd', 'foo.bar', 'hello.world']);
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect(['a' => 1, 'hello' => 'world', 'c'])->not->toHaveKeys(['hello', 'c']);
expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => 'baz']])->not->toHaveKeys(['foo.bar', 'c', 'z']);
})->throws(ExpectationFailedException::class);