diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index f802bc11..38a6c474 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -1131,6 +1131,22 @@ public function toBeUuid(string $message = ''): self return $this; } + /** + * Asserts that the value is ULID. + * + * @return self + */ + public function toBeUlid(string $message = ''): self + { + if (! is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + + Assert::assertTrue(Str::isUlid($this->value), $message); + + return $this; + } + /** * Asserts that the value is between 2 specified values * diff --git a/src/Support/Str.php b/src/Support/Str.php index 0e654bc8..022e0a01 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -101,6 +101,14 @@ public static function isUuid(string $value): bool return preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0; } + /** + * Determine if a given value is a valid ULID. + */ + public static function isUlid(string $value): bool + { + return preg_match('/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/iD', $value) > 0; + } + /** * Creates a describe block as `$describeDescription` → `$testDescription` format. * diff --git a/tests/Features/Expect/toBeUlid.php b/tests/Features/Expect/toBeUlid.php new file mode 100644 index 00000000..0f4d2742 --- /dev/null +++ b/tests/Features/Expect/toBeUlid.php @@ -0,0 +1,24 @@ +toBeUlid(); +})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].'); + +test('pass', function () { + expect('01JEWKD14JZJC6GKVQG8CAF93G')->toBeUlid(); +}); + +test('failures', function () { + expect('foo')->toBeUlid(); +})->throws(ExpectationFailedException::class); + +test('failures with message', function () { + expect('bar')->toBeUlid('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () { + expect('foo')->not->toBeUlid(); +});