diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index 1566b59c..8f806e0e 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -660,7 +660,7 @@ final class Expectation { foreach ($keys as $k => $key) { if (is_array($key)) { - $this->toHaveKeys(array_keys(Arr::dot($key, $k.'.')), $message); + $this->toHaveKeys(array_keys(Arr::dot($key, $k . '.')), $message); } else { $this->toHaveKey($key, message: $message); } @@ -1158,4 +1158,21 @@ final class Expectation return $this; } + + /** + * Asserts that the value can be converted to a slug + * + * @return self + */ + public function toBeSlug(string $message = ''): self + { + if ($message === '') { + $message = "Failed asserting that {$this->value} can be converted to a slug."; + } + + $slug = Str::slugify((string) $this->value); + Assert::assertNotEmpty($slug, $message); + + return $this; + } } diff --git a/src/Support/Str.php b/src/Support/Str.php index 67cff796..effebabf 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -58,7 +58,7 @@ final class Str { $code = str_replace('_', '__', $code); - $code = self::PREFIX.str_replace(' ', '_', $code); + $code = self::PREFIX . str_replace(' ', '_', $code); // sticks to PHP8.2 function naming rules https://www.php.net/manual/en/functions.user-defined.php return (string) preg_replace('/[^a-zA-Z0-9_\x80-\xff]/', '_', $code); @@ -117,4 +117,13 @@ final class Str { return (bool) filter_var($value, FILTER_VALIDATE_URL); } + + /** + * Converts the given `$target` to a URL-friendly "slug". + */ + public static function slugify(string $target): string + { + $target = preg_replace('/[^a-zA-Z0-9]+/', '-', $target); + return strtolower(trim($target, '-')); + } } diff --git a/tests/Features/Expect/toBeSlug.php b/tests/Features/Expect/toBeSlug.php new file mode 100644 index 00000000..2d7c19f8 --- /dev/null +++ b/tests/Features/Expect/toBeSlug.php @@ -0,0 +1,24 @@ +toBeSlug() + ->and('Another Test String')->toBeSlug(); +}); + +test('failures', function () { + expect('')->toBeSlug(); +})->throws(ExpectationFailedException::class); + +test('failures with custom message', function () { + expect('')->toBeSlug('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('failures with default message', function () { + expect('')->toBeSlug(); +})->throws(ExpectationFailedException::class, 'Failed asserting that can be converted to a slug.'); + +test('not failures', function () { + expect('This is a Test String!')->not->toBeSlug(); +})->throws(ExpectationFailedException::class);