From a5ce2dc1a1e3494f652633762d911067ec40d203 Mon Sep 17 00:00:00 2001 From: luke Date: Wed, 16 Jun 2021 20:27:36 +0100 Subject: [PATCH 1/8] Reimplements the new `toHaveArray` features. --- src/Expectation.php | 12 ++++- src/Support/Arr.php | 68 ++++++++++++++++++++++++++++ tests/.snapshots/success.txt | 17 ++++++- tests/Features/Expect/toHaveKey.php | 68 +++++++++++++++++++++++++--- tests/Features/Expect/toHaveKeys.php | 6 +-- 5 files changed, 158 insertions(+), 13 deletions(-) create mode 100644 src/Support/Arr.php diff --git a/src/Expectation.php b/src/Expectation.php index e35acc5f..45b52891 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -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; /** @@ -522,10 +524,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; diff --git a/src/Support/Arr.php b/src/Support/Arr.php new file mode 100644 index 00000000..ef20f862 --- /dev/null +++ b/src/Support/Arr.php @@ -0,0 +1,68 @@ + $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 $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; + } +} diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index 9f57d4fa..05d2cc3a 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -336,8 +336,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 @@ -554,5 +569,5 @@ ✓ it is a test ✓ it uses correct parent class - Tests: 4 incompleted, 7 skipped, 340 passed + Tests: 4 incompleted, 7 skipped, 355 passed \ No newline at end of file diff --git a/tests/Features/Expect/toHaveKey.php b/tests/Features/Expect/toHaveKey.php index 1695688b..01383c91 100644 --- a/tests/Features/Expect/toHaveKey.php +++ b/tests/Features/Expect/toHaveKey.php @@ -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); diff --git a/tests/Features/Expect/toHaveKeys.php b/tests/Features/Expect/toHaveKeys.php index a09c8809..4c29a431 100644 --- a/tests/Features/Expect/toHaveKeys.php +++ b/tests/Features/Expect/toHaveKeys.php @@ -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); From c1b27579ca791d3728f211c862448c4398f3e44f Mon Sep 17 00:00:00 2001 From: Daniel Ang Date: Fri, 18 Jun 2021 12:43:17 +0200 Subject: [PATCH 2/8] Method json() to parse JSON strings - Parse a JSON string into array - Test --- src/Expectation.php | 11 +++++++++++ tests/Features/Expect/json.php | 14 ++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/Features/Expect/json.php diff --git a/src/Expectation.php b/src/Expectation.php index 45b52891..3c238805 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -60,6 +60,17 @@ final class Expectation return new self($value); } + /** + * Parses Json String to Array. + */ + public function json(): Expectation + { + Assert::assertIsString($this->value); + Assert::assertJson($this->value); + + return new self(json_decode($this->value, true)); + } + /** * Dump the expectation value and end the script. * diff --git a/tests/Features/Expect/json.php b/tests/Features/Expect/json.php new file mode 100644 index 00000000..2228fd56 --- /dev/null +++ b/tests/Features/Expect/json.php @@ -0,0 +1,14 @@ +json() + ->name + ->toBe('Nuno'); +}); + +test('fails with broken json string', function () { + expect('{":"Nuno"}')->json(); +})->throws(ExpectationFailedException::class); From ecb37fce91d6ddcf55bf9d5d6c4176127340ad35 Mon Sep 17 00:00:00 2001 From: Daniel Ang Date: Fri, 18 Jun 2021 13:04:31 +0200 Subject: [PATCH 3/8] Refactoring to use toBeJson() Refactoring according to @lukeraymonddowning suggestion --- src/Expectation.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Expectation.php b/src/Expectation.php index 3c238805..e2d6369b 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -65,10 +65,7 @@ final class Expectation */ public function json(): Expectation { - Assert::assertIsString($this->value); - Assert::assertJson($this->value); - - return new self(json_decode($this->value, true)); + return $this->toBeJson()->and(json_decode($this->value, true)); } /** From 4bf69b97bdb20844b36f902535ba9a8eb2771f19 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 18 Jun 2021 12:06:41 +0100 Subject: [PATCH 4/8] feat(json): updates docs --- src/Expectation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Expectation.php b/src/Expectation.php index e2d6369b..85cb0094 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -61,7 +61,7 @@ final class Expectation } /** - * Parses Json String to Array. + * Creates a new expectation with the decoded JSON value. */ public function json(): Expectation { From 3afdedbd3f3e486483073fc0cacf87fb3ada454a Mon Sep 17 00:00:00 2001 From: Daniel Ang Date: Fri, 18 Jun 2021 13:11:37 +0200 Subject: [PATCH 5/8] Snapshots --- tests/.snapshots/success.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index 05d2cc3a..422bfc2f 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -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 @@ -569,5 +573,5 @@ ✓ it is a test ✓ it uses correct parent class - Tests: 4 incompleted, 7 skipped, 355 passed + Tests: 4 incompleted, 7 skipped, 357 passed \ No newline at end of file From db9f1254b5f40d20525b3bef096fb4c03bd32c6e Mon Sep 17 00:00:00 2001 From: luke Date: Fri, 18 Jun 2021 12:30:57 +0100 Subject: [PATCH 6/8] Tags Pest 1.6 --- src/Pest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pest.php b/src/Pest.php index 759cb913..847a15fc 100644 --- a/src/Pest.php +++ b/src/Pest.php @@ -6,7 +6,7 @@ namespace Pest; function version(): string { - return '1.5.0'; + return '1.6.0'; } function testDirectory(string $file = ''): string From 9b34650e7297c2c506bc88d7528e4e4aba132a77 Mon Sep 17 00:00:00 2001 From: luke Date: Fri, 18 Jun 2021 12:36:43 +0100 Subject: [PATCH 7/8] Adds changelog for 1.6.0 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee5a3af9..ab56a6b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)) From b6f0496c3c071a897c054be449d98a74ef850dd4 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 18 Jun 2021 12:39:05 +0100 Subject: [PATCH 8/8] chore: updates funding --- .github/FUNDING.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 341f601a..8b8b5939 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -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