From f0157210b21960be4ca0f73e5de4999b9d6cdd9b Mon Sep 17 00:00:00 2001 From: Tiago Santos Date: Fri, 25 Sep 2026 23:58:00 +0100 Subject: [PATCH 1/2] fix: check each target in negated toUse A skipped class in expect([...]) made the positive rule fail, so the negation passed even when another class used the dependency. Fixes #1751 --- src/Expectations/OppositeExpectation.php | 22 +++++++++-- tests/Features/Expect/notToUse.php | 38 +++++++++++++++++++ tests/Fixtures/Arch/NotToUse/AlsoClean.php | 7 ++++ tests/Fixtures/Arch/NotToUse/Clean.php | 7 ++++ tests/Fixtures/Arch/NotToUse/Dependency.php | 7 ++++ .../Arch/NotToUse/OtherDependency.php | 7 ++++ .../Fixtures/Arch/NotToUse/UsesDependency.php | 13 +++++++ 7 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 tests/Features/Expect/notToUse.php create mode 100644 tests/Fixtures/Arch/NotToUse/AlsoClean.php create mode 100644 tests/Fixtures/Arch/NotToUse/Clean.php create mode 100644 tests/Fixtures/Arch/NotToUse/Dependency.php create mode 100644 tests/Fixtures/Arch/NotToUse/OtherDependency.php create mode 100644 tests/Fixtures/Arch/NotToUse/UsesDependency.php diff --git a/src/Expectations/OppositeExpectation.php b/src/Expectations/OppositeExpectation.php index b85150a07..7579ccb39 100644 --- a/src/Expectations/OppositeExpectation.php +++ b/src/Expectations/OppositeExpectation.php @@ -11,7 +11,6 @@ use Pest\Arch\Expectations\ToUse; use Pest\Arch\GroupArchExpectation; use Pest\Arch\PendingArchExpectation; -use Pest\Arch\SingleArchExpectation; use Pest\Arch\Support\FileLineFinder; use Pest\Exceptions\InvalidExpectation; use Pest\Exceptions\MissingDependency; @@ -72,9 +71,24 @@ public function toUse(array|string $targets): ArchExpectation /** @var Expectation|string> $original */ $original = $this->original; - return GroupArchExpectation::fromExpectations($original, array_map(fn (string $target): SingleArchExpectation => ToUse::make($original, $target)->opposite( - fn () => $this->throwExpectationFailedException('toUse', $target), - ), is_string($targets) ? [$targets] : $targets)); + /** @var array $subjects */ + $subjects = is_string($original->value) ? [$original->value] : $original->value; + $dependencies = is_string($targets) ? [$targets] : $targets; + + $expectations = []; + + foreach ($subjects as $subject) { + $subjectExpectation = new Expectation($original->value); + $subjectExpectation->value = $subject; + + foreach ($dependencies as $dependency) { + $expectations[] = ToUse::make($subjectExpectation, $dependency)->opposite( + fn () => $this->throwExpectationFailedException('toUse', $dependency), + ); + } + } + + return GroupArchExpectation::fromExpectations($original, $expectations); } public function toHaveFileSystemPermissions(string $permissions): ArchExpectation diff --git a/tests/Features/Expect/notToUse.php b/tests/Features/Expect/notToUse.php new file mode 100644 index 000000000..9229ef99c --- /dev/null +++ b/tests/Features/Expect/notToUse.php @@ -0,0 +1,38 @@ +throws(ExpectationFailedException::class) + ->expect(UsesDependency::class) + ->not->toUse(Dependency::class); + +test('single target negation passes when the target does not use the dependency') + ->expect(Clean::class) + ->not->toUse(Dependency::class); + +test('multi target negation fails when any target uses the dependency') + ->throws(ExpectationFailedException::class) + ->expect([UsesDependency::class, Clean::class]) + ->not->toUse(Dependency::class); + +test('multi target negation passes when no target uses the dependency') + ->expect([Clean::class, AlsoClean::class]) + ->not->toUse(Dependency::class); + +test('multi target negation fails when any target uses any listed dependency') + ->throws(ExpectationFailedException::class) + ->expect([UsesDependency::class, Clean::class]) + ->not->toUse([Dependency::class, OtherDependency::class]); + +test('positive to use still fails when one target skips the dependency') + ->throws(ExpectationFailedException::class) + ->expect([UsesDependency::class, Clean::class]) + ->toUse(Dependency::class); diff --git a/tests/Fixtures/Arch/NotToUse/AlsoClean.php b/tests/Fixtures/Arch/NotToUse/AlsoClean.php new file mode 100644 index 000000000..0f903344a --- /dev/null +++ b/tests/Fixtures/Arch/NotToUse/AlsoClean.php @@ -0,0 +1,7 @@ + Date: Mon, 26 Oct 2026 09:14:06 +0000 Subject: [PATCH 2/2] test: update snapshots for notToUse Negated toUse coverage adds 6 tests and 24 assertions, so the success snapshot and parallel totals were stale. --- tests/.snapshots/success.txt | 10 +++++++++- tests/Visual/Parallel.php | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index fd0696710..abf81c426 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -435,6 +435,14 @@ PASS Tests\Features\Expect\not ✓ not property calls + PASS Tests\Features\Expect\notToUse + ✓ single target negation fails when the target uses the dependency + ✓ single target negation passes when the target does not use the dependency + ✓ multi target negation fails when any target uses the dependency + ✓ multi target negation passes when no target uses the dependency + ✓ multi target negation fails when any target uses any listed dependency + ✓ positive to use still fails when one target skips the dependency + PASS Tests\Features\Expect\pipes ✓ pipe is applied and can stop pipeline ✓ pipe is run and can let the pipeline keep going @@ -2274,4 +2282,4 @@ ✓ pass with dataset with ('my-datas-set-value') ✓ within describe → pass with dataset with ('my-datas-set-value') - Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1613 passed (3519 assertions) \ No newline at end of file + Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1619 passed (3543 assertions) \ No newline at end of file diff --git a/tests/Visual/Parallel.php b/tests/Visual/Parallel.php index 6f42099a9..ab317dfce 100644 --- a/tests/Visual/Parallel.php +++ b/tests/Visual/Parallel.php @@ -20,7 +20,7 @@ $output = $run('--exclude-group=integration'); $output = implode("\n", array_slice(explode("\n", (string) $output), -10)); - $expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1594 passed (3461 assertions)'; + $expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1600 passed (3485 assertions)'; expect($output) ->toContain("Tests: {$expected}")