From 4951b1b0f9a20ab50d4e534806b1ee22ee9d2c6c Mon Sep 17 00:00:00 2001 From: Maurizio Date: Thu, 17 Aug 2023 20:48:18 +0200 Subject: [PATCH 01/10] feat: add `toBeSnakeCase` --- src/Mixins/Expectation.php | 12 ++++++++++++ tests/Features/Expect/toBeSnakeCase.php | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/Features/Expect/toBeSnakeCase.php diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index 56e1da09..d128d30b 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -1009,4 +1009,16 @@ final class Expectation return $this; } + + /** + * Asserts that the value is snake_case. + * + * @return self + */ + public function toBeSnakeCase(string $message = ''): self + { + Assert::assertTrue((bool) preg_match('/^[\p{Ll}_]+$/u', (string) $this->value), $message); + + return $this; + } } diff --git a/tests/Features/Expect/toBeSnakeCase.php b/tests/Features/Expect/toBeSnakeCase.php new file mode 100644 index 00000000..121f9e51 --- /dev/null +++ b/tests/Features/Expect/toBeSnakeCase.php @@ -0,0 +1,23 @@ +toBeSnakeCase(); + expect('abc_def')->toBeSnakeCase(); + expect('abc-def')->not->toBeSnakeCase(); + expect('abcDef')->not->toBeSnakeCase(); + expect('AbcDef')->not->toBeSnakeCase(); +}); + +test('failures', function () { + expect('Abc')->toBeSnakeCase(); +})->throws(ExpectationFailedException::class); + +test('failures with custom message', function () { + expect('Abc')->toBeSnakeCase('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () { + expect('abc_def')->not->toBeSnakeCase(); +})->throws(ExpectationFailedException::class); From d2096df82a1a91babeffaf1a0efc1030b28c5c8e Mon Sep 17 00:00:00 2001 From: Maurizio Date: Thu, 17 Aug 2023 20:48:51 +0200 Subject: [PATCH 02/10] feat: add `toBeKebabCase` --- src/Mixins/Expectation.php | 12 ++++++++++++ tests/Features/Expect/toBeKebabCase.php | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/Features/Expect/toBeKebabCase.php diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index d128d30b..0686ad80 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -1021,4 +1021,16 @@ final class Expectation return $this; } + + /** + * Asserts that the value is kebab-case. + * + * @return self + */ + public function toBeKebabCase(string $message = ''): self + { + Assert::assertTrue((bool) preg_match('/^[\p{Ll}-]+$/u', (string) $this->value), $message); + + return $this; + } } diff --git a/tests/Features/Expect/toBeKebabCase.php b/tests/Features/Expect/toBeKebabCase.php new file mode 100644 index 00000000..1ca54ddc --- /dev/null +++ b/tests/Features/Expect/toBeKebabCase.php @@ -0,0 +1,23 @@ +toBeKebabCase(); + expect('abc-def')->toBeKebabCase(); + expect('abc_def')->not->toBeKebabCase(); + expect('abcDef')->not->toBeKebabCase(); + expect('AbcDef')->not->toBeKebabCase(); +}); + +test('failures', function () { + expect('Abc')->toBeKebabCase(); +})->throws(ExpectationFailedException::class); + +test('failures with custom message', function () { + expect('Abc')->toBeKebabCase('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () { + expect('abc-def')->not->toBeKebabCase(); +})->throws(ExpectationFailedException::class); From a76414aeeee731911025f105af54cba08d5f1393 Mon Sep 17 00:00:00 2001 From: Maurizio Date: Thu, 17 Aug 2023 20:49:21 +0200 Subject: [PATCH 03/10] feat: add `toBeCamelCase` --- src/Mixins/Expectation.php | 12 ++++++++++++ tests/Features/Expect/toBeCamelCase.php | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/Features/Expect/toBeCamelCase.php diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index 0686ad80..d667f854 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -1033,4 +1033,16 @@ final class Expectation return $this; } + + /** + * Asserts that the value is camelCase. + * + * @return self + */ + public function toBeCamelCase(string $message = ''): self + { + Assert::assertTrue((bool) preg_match('/^\p{Ll}[\p{Ll}\p{Lu}]+$/u', (string) $this->value), $message); + + return $this; + } } diff --git a/tests/Features/Expect/toBeCamelCase.php b/tests/Features/Expect/toBeCamelCase.php new file mode 100644 index 00000000..d2b38433 --- /dev/null +++ b/tests/Features/Expect/toBeCamelCase.php @@ -0,0 +1,23 @@ +toBeCamelCase(); + expect('abcDef')->toBeCamelCase(); + expect('abc-def')->not->toBeCamelCase(); + expect('abc-def')->not->toBeCamelCase(); + expect('AbcDef')->not->toBeCamelCase(); +}); + +test('failures', function () { + expect('Abc')->toBeCamelCase(); +})->throws(ExpectationFailedException::class); + +test('failures with custom message', function () { + expect('Abc')->toBeCamelCase('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () { + expect('abcDef')->not->toBeCamelCase(); +})->throws(ExpectationFailedException::class); From 997b0e936854c9cad887cc9b3c809bd9c3437ed6 Mon Sep 17 00:00:00 2001 From: Maurizio Date: Thu, 17 Aug 2023 20:49:40 +0200 Subject: [PATCH 04/10] feat: add `toBeStudlyCase` --- src/Mixins/Expectation.php | 12 ++++++++++++ tests/Features/Expect/toBeStudlyCase.php | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/Features/Expect/toBeStudlyCase.php diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index d667f854..a51718a0 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -1045,4 +1045,16 @@ final class Expectation return $this; } + + /** + * Asserts that the value is StudlyCase. + * + * @return self + */ + public function toBeStudlyCase(string $message = ''): self + { + Assert::assertTrue((bool) preg_match('/^\p{Lu}+\p{Ll}[\p{Ll}\p{Lu}]+$/u', (string) $this->value), $message); + + return $this; + } } diff --git a/tests/Features/Expect/toBeStudlyCase.php b/tests/Features/Expect/toBeStudlyCase.php new file mode 100644 index 00000000..af6391dc --- /dev/null +++ b/tests/Features/Expect/toBeStudlyCase.php @@ -0,0 +1,23 @@ +toBeStudlyCase(); + expect('AbcDef')->toBeStudlyCase(); + expect('abc-def')->not->toBeStudlyCase(); + expect('abc-def')->not->toBeStudlyCase(); + expect('abc')->not->toBeStudlyCase(); +}); + +test('failures', function () { + expect('abc')->toBeStudlyCase(); +})->throws(ExpectationFailedException::class); + +test('failures with custom message', function () { + expect('abc')->toBeStudlyCase('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () { + expect('AbcDef')->not->toBeStudlyCase(); +})->throws(ExpectationFailedException::class); From 0b246f7a76655f7f0aa1211dbe47e0d1e15ecace Mon Sep 17 00:00:00 2001 From: Maurizio Date: Thu, 17 Aug 2023 20:50:26 +0200 Subject: [PATCH 05/10] feat: add `toHaveSnakeCaseKeys` --- src/Expectation.php | 22 +++++++++++++ tests/Features/Expect/toHaveSnakeCaseKeys.php | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/Features/Expect/toHaveSnakeCaseKeys.php diff --git a/src/Expectation.php b/src/Expectation.php index 476cee1d..148e44b1 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -740,4 +740,26 @@ final class Expectation FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')) ); } + + /** + * Asserts that the given expectation is iterable and contains snake_case keys. + * + * @return self + */ + public function toHaveSnakeCaseKeys(string $message = ''): self + { + if (! is_iterable($this->value)) { + InvalidExpectationValue::expected('iterable'); + } + + foreach ($this->value as $k => $item) { + $this->and($k)->toBeSnakeCase($message); + + if (is_array($item)) { + $this->and($item)->toHaveSnakeCaseKeys($message); + } + } + + return $this; + } } diff --git a/tests/Features/Expect/toHaveSnakeCaseKeys.php b/tests/Features/Expect/toHaveSnakeCaseKeys.php new file mode 100644 index 00000000..584a4b65 --- /dev/null +++ b/tests/Features/Expect/toHaveSnakeCaseKeys.php @@ -0,0 +1,31 @@ + true, + 'snake_case' => [ + 'snake' => true, + 'snake_case' => [ + 'snake' => true, + 'snake_case' => true, + ], + ], +]; + +test('pass', function () use ($array) { + expect($array)->toHaveSnakeCaseKeys(); +}); + +test('failures', function () { + expect('not-an-array')->toHaveSnakeCaseKeys(); +})->throws(InvalidExpectationValue::class); + +test('failures with message', function () use ($array) { + expect($array)->not->toHaveSnakeCaseKeys('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () use ($array) { + expect($array)->not->toHaveSnakeCaseKeys(); +})->throws(ExpectationFailedException::class); From 0b115230f9cee536a3ef8d173e7f872546603e45 Mon Sep 17 00:00:00 2001 From: Maurizio Date: Thu, 17 Aug 2023 20:50:51 +0200 Subject: [PATCH 06/10] feat: add `toHaveKebabCaseKeys` --- src/Expectation.php | 22 +++++++++++++ tests/Features/Expect/toHaveKebabCaseKeys.php | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/Features/Expect/toHaveKebabCaseKeys.php diff --git a/src/Expectation.php b/src/Expectation.php index 148e44b1..c5f4c411 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -762,4 +762,26 @@ final class Expectation return $this; } + + /** + * Asserts that the given expectation is iterable and contains kebab-case keys. + * + * @return self + */ + public function toHaveKebabCaseKeys(string $message = ''): self + { + if (! is_iterable($this->value)) { + InvalidExpectationValue::expected('iterable'); + } + + foreach ($this->value as $k => $item) { + $this->and($k)->toBeKebabCase($message); + + if (is_array($item)) { + $this->and($item)->toHaveKebabCaseKeys($message); + } + } + + return $this; + } } diff --git a/tests/Features/Expect/toHaveKebabCaseKeys.php b/tests/Features/Expect/toHaveKebabCaseKeys.php new file mode 100644 index 00000000..cb16bc93 --- /dev/null +++ b/tests/Features/Expect/toHaveKebabCaseKeys.php @@ -0,0 +1,31 @@ + true, + 'kebab-case' => [ + 'kebab' => true, + 'kebab-case' => [ + 'kebab' => true, + 'kebab-case' => true, + ], + ], +]; + +test('pass', function () use ($array) { + expect($array)->toHaveKebabCaseKeys(); +}); + +test('failures', function () { + expect('not-an-array')->toHaveKebabCaseKeys(); +})->throws(InvalidExpectationValue::class); + +test('failures with message', function () use ($array) { + expect($array)->not->toHaveKebabCaseKeys('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () use ($array) { + expect($array)->not->toHaveKebabCaseKeys(); +})->throws(ExpectationFailedException::class); From 5d81cf0d4c3aa2ad67a0933b8f6a2c4623468d1e Mon Sep 17 00:00:00 2001 From: Maurizio Date: Thu, 17 Aug 2023 20:51:14 +0200 Subject: [PATCH 07/10] feat: add `toHaveCamelCaseKeys` --- src/Expectation.php | 22 +++++++++++++ tests/Features/Expect/toHaveCamelCaseKeys.php | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/Features/Expect/toHaveCamelCaseKeys.php diff --git a/src/Expectation.php b/src/Expectation.php index c5f4c411..df725059 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -784,4 +784,26 @@ final class Expectation return $this; } + + /** + * Asserts that the given expectation is iterable and contains camelCase keys. + * + * @return self + */ + public function toHaveCamelCaseKeys(string $message = ''): self + { + if (! is_iterable($this->value)) { + InvalidExpectationValue::expected('iterable'); + } + + foreach ($this->value as $k => $item) { + $this->and($k)->toBeCamelCase($message); + + if (is_array($item)) { + $this->and($item)->toHaveCamelCaseKeys($message); + } + } + + return $this; + } } diff --git a/tests/Features/Expect/toHaveCamelCaseKeys.php b/tests/Features/Expect/toHaveCamelCaseKeys.php new file mode 100644 index 00000000..4fc73428 --- /dev/null +++ b/tests/Features/Expect/toHaveCamelCaseKeys.php @@ -0,0 +1,31 @@ + true, + 'camelCase' => [ + 'camel' => true, + 'camelCase' => [ + 'camel' => true, + 'camelCase' => true, + ], + ], +]; + +test('pass', function () use ($array) { + expect($array)->toHaveCamelCaseKeys(); +}); + +test('failures', function () { + expect('not-an-array')->toHaveCamelCaseKeys(); +})->throws(InvalidExpectationValue::class); + +test('failures with message', function () use ($array) { + expect($array)->not->toHaveCamelCaseKeys('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () use ($array) { + expect($array)->not->toHaveCamelCaseKeys(); +})->throws(ExpectationFailedException::class); From 478144fb353ed7ecbecea1601081613e1bad087a Mon Sep 17 00:00:00 2001 From: Maurizio Date: Thu, 17 Aug 2023 20:51:26 +0200 Subject: [PATCH 08/10] feat: add `toHaveStudlyCaseKeys` --- src/Expectation.php | 22 +++++++++++++ .../Features/Expect/toHaveStudlyCaseKeys.php | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/Features/Expect/toHaveStudlyCaseKeys.php diff --git a/src/Expectation.php b/src/Expectation.php index df725059..4f583004 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -806,4 +806,26 @@ final class Expectation return $this; } + + /** + * Asserts that the given expectation is iterable and contains StudlyCase keys. + * + * @return self + */ + public function toHaveStudlyCaseKeys(string $message = ''): self + { + if (! is_iterable($this->value)) { + InvalidExpectationValue::expected('iterable'); + } + + foreach ($this->value as $k => $item) { + $this->and($k)->toBeStudlyCase($message); + + if (is_array($item)) { + $this->and($item)->toHaveStudlyCaseKeys($message); + } + } + + return $this; + } } diff --git a/tests/Features/Expect/toHaveStudlyCaseKeys.php b/tests/Features/Expect/toHaveStudlyCaseKeys.php new file mode 100644 index 00000000..71c0713a --- /dev/null +++ b/tests/Features/Expect/toHaveStudlyCaseKeys.php @@ -0,0 +1,31 @@ + true, + 'StudlyCase' => [ + 'Studly' => true, + 'StudlyCase' => [ + 'Studly' => true, + 'StudlyCase' => true, + ], + ], +]; + +test('pass', function () use ($array) { + expect($array)->toHaveStudlyCaseKeys(); +}); + +test('failures', function () { + expect('not-an-array')->toHaveStudlyCaseKeys(); +})->throws(InvalidExpectationValue::class); + +test('failures with message', function () use ($array) { + expect($array)->not->toHaveStudlyCaseKeys('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () use ($array) { + expect($array)->not->toHaveStudlyCaseKeys(); +})->throws(ExpectationFailedException::class); From f8dd28621348aed083dd5d2c7496f57b9ec80131 Mon Sep 17 00:00:00 2001 From: Maurizio Date: Sat, 19 Aug 2023 10:27:02 +0200 Subject: [PATCH 09/10] chore: skip array list --- src/Expectation.php | 16 ++++++++++++---- tests/Features/Expect/toHaveCamelCaseKeys.php | 5 +++++ tests/Features/Expect/toHaveKebabCaseKeys.php | 5 +++++ tests/Features/Expect/toHaveSnakeCaseKeys.php | 5 +++++ tests/Features/Expect/toHaveStudlyCaseKeys.php | 5 +++++ 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/Expectation.php b/src/Expectation.php index 4f583004..fcfe88d5 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -753,7 +753,9 @@ final class Expectation } foreach ($this->value as $k => $item) { - $this->and($k)->toBeSnakeCase($message); + if (is_string($k)) { + $this->and($k)->toBeSnakeCase($message); + } if (is_array($item)) { $this->and($item)->toHaveSnakeCaseKeys($message); @@ -775,7 +777,9 @@ final class Expectation } foreach ($this->value as $k => $item) { - $this->and($k)->toBeKebabCase($message); + if (is_string($k)) { + $this->and($k)->toBeKebabCase($message); + } if (is_array($item)) { $this->and($item)->toHaveKebabCaseKeys($message); @@ -797,7 +801,9 @@ final class Expectation } foreach ($this->value as $k => $item) { - $this->and($k)->toBeCamelCase($message); + if (is_string($k)) { + $this->and($k)->toBeCamelCase($message); + } if (is_array($item)) { $this->and($item)->toHaveCamelCaseKeys($message); @@ -819,7 +825,9 @@ final class Expectation } foreach ($this->value as $k => $item) { - $this->and($k)->toBeStudlyCase($message); + if (is_string($k)) { + $this->and($k)->toBeStudlyCase($message); + } if (is_array($item)) { $this->and($item)->toHaveStudlyCaseKeys($message); diff --git a/tests/Features/Expect/toHaveCamelCaseKeys.php b/tests/Features/Expect/toHaveCamelCaseKeys.php index 4fc73428..cf465a99 100644 --- a/tests/Features/Expect/toHaveCamelCaseKeys.php +++ b/tests/Features/Expect/toHaveCamelCaseKeys.php @@ -11,6 +11,11 @@ $array = [ 'camel' => true, 'camelCase' => true, ], + 'list' => [ + 'abc', + 'def', + 'ghi', + ], ], ]; diff --git a/tests/Features/Expect/toHaveKebabCaseKeys.php b/tests/Features/Expect/toHaveKebabCaseKeys.php index cb16bc93..7dff565d 100644 --- a/tests/Features/Expect/toHaveKebabCaseKeys.php +++ b/tests/Features/Expect/toHaveKebabCaseKeys.php @@ -11,6 +11,11 @@ $array = [ 'kebab' => true, 'kebab-case' => true, ], + 'list' => [ + 'abc', + 'def', + 'ghi', + ], ], ]; diff --git a/tests/Features/Expect/toHaveSnakeCaseKeys.php b/tests/Features/Expect/toHaveSnakeCaseKeys.php index 584a4b65..f43d91c4 100644 --- a/tests/Features/Expect/toHaveSnakeCaseKeys.php +++ b/tests/Features/Expect/toHaveSnakeCaseKeys.php @@ -11,6 +11,11 @@ $array = [ 'snake' => true, 'snake_case' => true, ], + 'list' => [ + 'abc', + 'def', + 'ghi', + ], ], ]; diff --git a/tests/Features/Expect/toHaveStudlyCaseKeys.php b/tests/Features/Expect/toHaveStudlyCaseKeys.php index 71c0713a..caab167e 100644 --- a/tests/Features/Expect/toHaveStudlyCaseKeys.php +++ b/tests/Features/Expect/toHaveStudlyCaseKeys.php @@ -11,6 +11,11 @@ $array = [ 'Studly' => true, 'StudlyCase' => true, ], + 'List' => [ + 'abc', + 'def', + 'ghi', + ], ], ]; From de1c721cd90828190591fb16fe927b57c1b95df3 Mon Sep 17 00:00:00 2001 From: Maurizio Date: Sat, 19 Aug 2023 10:27:21 +0200 Subject: [PATCH 10/10] chore: improve error messages --- src/Mixins/Expectation.php | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index a51718a0..74cd066a 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -1017,7 +1017,13 @@ final class Expectation */ public function toBeSnakeCase(string $message = ''): self { - Assert::assertTrue((bool) preg_match('/^[\p{Ll}_]+$/u', (string) $this->value), $message); + $value = (string) $this->value; + + if ($message === '') { + $message = "Failed asserting that {$value} is snake_case."; + } + + Assert::assertTrue((bool) preg_match('/^[\p{Ll}_]+$/u', $value), $message); return $this; } @@ -1029,7 +1035,13 @@ final class Expectation */ public function toBeKebabCase(string $message = ''): self { - Assert::assertTrue((bool) preg_match('/^[\p{Ll}-]+$/u', (string) $this->value), $message); + $value = (string) $this->value; + + if ($message === '') { + $message = "Failed asserting that {$value} is kebab-case."; + } + + Assert::assertTrue((bool) preg_match('/^[\p{Ll}-]+$/u', $value), $message); return $this; } @@ -1041,7 +1053,13 @@ final class Expectation */ public function toBeCamelCase(string $message = ''): self { - Assert::assertTrue((bool) preg_match('/^\p{Ll}[\p{Ll}\p{Lu}]+$/u', (string) $this->value), $message); + $value = (string) $this->value; + + if ($message === '') { + $message = "Failed asserting that {$value} is camelCase."; + } + + Assert::assertTrue((bool) preg_match('/^\p{Ll}[\p{Ll}\p{Lu}]+$/u', $value), $message); return $this; } @@ -1053,7 +1071,13 @@ final class Expectation */ public function toBeStudlyCase(string $message = ''): self { - Assert::assertTrue((bool) preg_match('/^\p{Lu}+\p{Ll}[\p{Ll}\p{Lu}]+$/u', (string) $this->value), $message); + $value = (string) $this->value; + + if ($message === '') { + $message = "Failed asserting that {$value} is StudlyCase."; + } + + Assert::assertTrue((bool) preg_match('/^\p{Lu}+\p{Ll}[\p{Ll}\p{Lu}]+$/u', $value), $message); return $this; }