diff --git a/src/Expectations/OppositeExpectation.php b/src/Expectations/OppositeExpectation.php index 1ae9ccbe..a5b9b480 100644 --- a/src/Expectations/OppositeExpectation.php +++ b/src/Expectations/OppositeExpectation.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Pest\Expectations; use Pest\Expectation; +use Pest\Support\Arr; use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\Exporter\Exporter; @@ -29,15 +30,19 @@ final class OppositeExpectation /** * Asserts that the value array not has the provided $keys. * - * @param array $keys + * @param array> $keys * * @return Expectation */ public function toHaveKeys(array $keys): Expectation { - foreach ($keys as $key) { + foreach ($keys as $k => $key) { try { - $this->original->toHaveKey($key); + if (is_array($key)) { + $this->toHaveKeys(array_keys(Arr::dot($key, $k . '.'))); + } else { + $this->original->toHaveKey($key); + } } catch (ExpectationFailedException) { continue; } diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index 337cc9cc..71316264 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -595,14 +595,18 @@ final class Expectation /** * Asserts that the value array has the provided $keys. * - * @param array $keys + * @param array> $keys * * @return Expectation */ public function toHaveKeys(array $keys): Expectation { - foreach ($keys as $key) { - $this->toHaveKey($key); + foreach ($keys as $k => $key) { + if (is_array($key)) { + $this->toHaveKeys(array_keys(Arr::dot($key, $k . '.'))); + } else { + $this->toHaveKey($key); + } } return $this; diff --git a/src/Support/Arr.php b/src/Support/Arr.php index 922110e1..f4eb19e2 100644 --- a/src/Support/Arr.php +++ b/src/Support/Arr.php @@ -60,4 +60,26 @@ final class Arr return $array; } + + /** + * Flatten a multi-dimensional associative array with dots. + * + * @param array $array + * + * @return array + */ + public static function dot(array $array, string $prepend = ''): array + { + $results = []; + + foreach ($array as $key => $value) { + if (is_array($value) && count($value) > 0) { + $results = array_merge($results, static::dot($value, $prepend . $key . '.')); + } else { + $results[$prepend . $value] = $value; + } + } + + return $results; + } } diff --git a/tests/Features/Expect/toHaveKeys.php b/tests/Features/Expect/toHaveKeys.php index 4c29a431..6ab0f506 100644 --- a/tests/Features/Expect/toHaveKeys.php +++ b/tests/Features/Expect/toHaveKeys.php @@ -6,10 +6,22 @@ test('pass', function () { expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => 'baz']])->toHaveKeys(['a', 'c', 'foo.bar']); }); +test('pass with multi-dimensional arrays', function () { + expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => ['bir' => 'biz']]])->toHaveKeys(['a', 'c', 'foo' => ['bar' => ['bir']]]); +}); + test('failures', function () { expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => 'baz']])->toHaveKeys(['a', 'd', 'foo.bar', 'hello.world']); })->throws(ExpectationFailedException::class); +test('failures with multi-dimensional arrays', function () { + expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => ['bir' => 'biz']]])->toHaveKeys(['a', 'd', 'foo' => ['bar' => 'bir'], 'hello.world']); +})->throws(ExpectationFailedException::class); + test('not failures', function () { expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => 'baz']])->not->toHaveKeys(['foo.bar', 'c', 'z']); })->throws(ExpectationFailedException::class); + +test('not failures with multi-dimensional arrays', function () { + expect(['a' => 1, 'b', 'c' => 'world', 'foo' => ['bar' => ['bir' => 'biz']]])->not->toHaveKeys(['foo' => ['bar' => 'bir'], 'c', 'z']); +})->throws(ExpectationFailedException::class);