From 9d02b649e27a7d8f488248de2218513ff3a6777d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 25 Jun 2024 21:56:08 +0100 Subject: [PATCH] feat: adds `toUseTraits` expectation --- src/Expectation.php | 37 ++++++++++++++++++++++++ src/Expectations/OppositeExpectation.php | 35 ++++++++++++++++++++++ tests/Features/Expect/toUseTrait.php | 16 ++++++++++ 3 files changed, 88 insertions(+) create mode 100644 tests/Features/Expect/toUseTrait.php diff --git a/src/Expectation.php b/src/Expectation.php index a410cf1d..ac4c020c 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -611,6 +611,41 @@ final class Expectation ); } + /** + * Asserts that the given expectation target to use the given trait. + * + * @param string $trait + */ + public function toUseTrait(string $trait): ArchExpectation + { + return $this->toUseTraits($trait); + } + + /** + * Asserts that the given expectation target to use the given traits. + * + * @param array|string $traits + */ + public function toUseTraits(array|string $traits): ArchExpectation + { + $traits = is_array($traits) ? $traits : [$traits]; + + return Targeted::make( + $this, + function (ObjectDescription $object) use ($traits): bool { + foreach ($traits as $trait) { + if (! in_array($trait, $object->reflectionClass->getTraitNames(), true)) { + return false; + } + } + + return true; + }, + "to use traits '".implode("', '", $traits)."'", + FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), + ); + } + /** * Asserts that the given expectation target to not implement any interfaces. */ @@ -668,6 +703,8 @@ final class Expectation ); } + + /** * Asserts that the given expectation target to implement the given interfaces. * diff --git a/src/Expectations/OppositeExpectation.php b/src/Expectations/OppositeExpectation.php index b46f987b..d0107b79 100644 --- a/src/Expectations/OppositeExpectation.php +++ b/src/Expectations/OppositeExpectation.php @@ -252,6 +252,41 @@ final class OppositeExpectation ); } + /** + * Asserts that the given expectation target not to use the given trait. + * + * @param string $trait + */ + public function toUseTrait(string $trait): ArchExpectation + { + return $this->toUseTraits($trait); + } + + /** + * Asserts that the given expectation target not to use the given traits. + * + * @param array|string $traits + */ + public function toUseTraits(array|string $traits): ArchExpectation + { + $traits = is_array($traits) ? $traits : [$traits]; + + return Targeted::make( + $this->original, + function (ObjectDescription $object) use ($traits): bool { + foreach ($traits as $trait) { + if (in_array($trait, $object->reflectionClass->getTraitNames(), true)) { + return false; + } + } + + return true; + }, + "to use traits '" . implode("', '", $traits) . "'", + FileLineFinder::where(fn(string $line): bool => str_contains($line, 'class')), + ); + } + /** * Asserts that the given expectation target not to implement the given interfaces. * diff --git a/tests/Features/Expect/toUseTrait.php b/tests/Features/Expect/toUseTrait.php new file mode 100644 index 00000000..fd9c933f --- /dev/null +++ b/tests/Features/Expect/toUseTrait.php @@ -0,0 +1,16 @@ +toUseTrait('Pest\Concerns\Retrievable') + ->and('Pest\Expectations\EachExpectation')->not->toUseTrait('Pest\Concerns\Retrievable'); +}); + +test('failures', function () { + expect('Pest\Expectations\EachExpectation')->toUseTrait('Pest\Concerns\Foo'); +})->throws(ArchExpectationFailedException::class); + +test('not failures', function () { + expect('Pest\Expectations\HigherOrderExpectation')->not->toUseTrait('Pest\Concerns\Retrievable'); +})->throws(ArchExpectationFailedException::class);