diff --git a/src/Expectation.php b/src/Expectation.php index 476cee1d..fcfe88d5 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -740,4 +740,100 @@ 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) { + if (is_string($k)) { + $this->and($k)->toBeSnakeCase($message); + } + + if (is_array($item)) { + $this->and($item)->toHaveSnakeCaseKeys($message); + } + } + + 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) { + if (is_string($k)) { + $this->and($k)->toBeKebabCase($message); + } + + if (is_array($item)) { + $this->and($item)->toHaveKebabCaseKeys($message); + } + } + + 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) { + if (is_string($k)) { + $this->and($k)->toBeCamelCase($message); + } + + if (is_array($item)) { + $this->and($item)->toHaveCamelCaseKeys($message); + } + } + + 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) { + if (is_string($k)) { + $this->and($k)->toBeStudlyCase($message); + } + + if (is_array($item)) { + $this->and($item)->toHaveStudlyCaseKeys($message); + } + } + + return $this; + } } diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index cb3ec31d..6b69014b 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -1021,4 +1021,76 @@ final class Expectation return $this; } + + /** + * Asserts that the value is snake_case. + * + * @return self + */ + public function toBeSnakeCase(string $message = ''): self + { + $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; + } + + /** + * Asserts that the value is kebab-case. + * + * @return self + */ + public function toBeKebabCase(string $message = ''): self + { + $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; + } + + /** + * Asserts that the value is camelCase. + * + * @return self + */ + public function toBeCamelCase(string $message = ''): self + { + $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; + } + + /** + * Asserts that the value is StudlyCase. + * + * @return self + */ + public function toBeStudlyCase(string $message = ''): self + { + $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; + } } 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); 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); 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); 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); diff --git a/tests/Features/Expect/toHaveCamelCaseKeys.php b/tests/Features/Expect/toHaveCamelCaseKeys.php new file mode 100644 index 00000000..cf465a99 --- /dev/null +++ b/tests/Features/Expect/toHaveCamelCaseKeys.php @@ -0,0 +1,36 @@ + true, + 'camelCase' => [ + 'camel' => true, + 'camelCase' => [ + 'camel' => true, + 'camelCase' => true, + ], + 'list' => [ + 'abc', + 'def', + 'ghi', + ], + ], +]; + +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); diff --git a/tests/Features/Expect/toHaveKebabCaseKeys.php b/tests/Features/Expect/toHaveKebabCaseKeys.php new file mode 100644 index 00000000..7dff565d --- /dev/null +++ b/tests/Features/Expect/toHaveKebabCaseKeys.php @@ -0,0 +1,36 @@ + true, + 'kebab-case' => [ + 'kebab' => true, + 'kebab-case' => [ + 'kebab' => true, + 'kebab-case' => true, + ], + 'list' => [ + 'abc', + 'def', + 'ghi', + ], + ], +]; + +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); diff --git a/tests/Features/Expect/toHaveSnakeCaseKeys.php b/tests/Features/Expect/toHaveSnakeCaseKeys.php new file mode 100644 index 00000000..f43d91c4 --- /dev/null +++ b/tests/Features/Expect/toHaveSnakeCaseKeys.php @@ -0,0 +1,36 @@ + true, + 'snake_case' => [ + 'snake' => true, + 'snake_case' => [ + 'snake' => true, + 'snake_case' => true, + ], + 'list' => [ + 'abc', + 'def', + 'ghi', + ], + ], +]; + +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); diff --git a/tests/Features/Expect/toHaveStudlyCaseKeys.php b/tests/Features/Expect/toHaveStudlyCaseKeys.php new file mode 100644 index 00000000..caab167e --- /dev/null +++ b/tests/Features/Expect/toHaveStudlyCaseKeys.php @@ -0,0 +1,36 @@ + true, + 'StudlyCase' => [ + 'Studly' => true, + 'StudlyCase' => [ + 'Studly' => true, + 'StudlyCase' => true, + ], + 'List' => [ + 'abc', + 'def', + 'ghi', + ], + ], +]; + +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);