*/ final class OppositeExpectation { /** * Creates a new opposite expectation. * * @param Expectation $original */ public function __construct(private readonly Expectation $original) { } /** * Asserts that the value array not has the provided $keys. * * @param array> $keys * @return Expectation */ public function toHaveKeys(array $keys): Expectation { foreach ($keys as $k => $key) { try { if (is_array($key)) { $this->toHaveKeys(array_keys(Arr::dot($key, $k.'.'))); } else { $this->original->toHaveKey($key); } } catch (ExpectationFailedException) { continue; } $this->throwExpectationFailedException('toHaveKey', [$key]); } return $this->original; } /** * Asserts that the given expectation target does not use any of the given dependencies. * * @param array|string $targets */ public function toUse(array|string $targets): ArchExpectation { return GroupArchExpectation::fromExpectations($this->original, array_map(fn (string $target): SingleArchExpectation => ToUse::make($this->original, $target)->opposite( fn () => $this->throwExpectationFailedException('toUse', $target), ), is_string($targets) ? [$targets] : $targets)); } /** * @param array|string $targets */ public function toOnlyUse(array|string $targets): never { throw InvalidExpectation::fromMethods(['not', 'toOnlyUse']); } public function toUseNothing(): never { throw InvalidExpectation::fromMethods(['not', 'toUseNothing']); } /** * Asserts that the given expectation dependency is not used. */ public function toBeUsed(): ArchExpectation { return ToBeUsedOnNothing::make($this->original); } /** * Asserts that the given expectation dependency is not used by any of the given targets. * * @param array|string $targets */ public function toBeUsedOn(array|string $targets): ArchExpectation { return GroupArchExpectation::fromExpectations($this->original, array_map(fn (string $target): GroupArchExpectation => ToBeUsedOn::make($this->original, $target)->opposite( fn () => $this->throwExpectationFailedException('toBeUsedOn', $target), ), is_string($targets) ? [$targets] : $targets)); } public function toOnlyBeUsedOn(): never { throw InvalidExpectation::fromMethods(['not', 'toOnlyBeUsedOn']); } /** * Asserts that the given expectation dependency is not used. */ public function toBeUsedOnNothing(): never { throw InvalidExpectation::fromMethods(['not', 'toBeUsedOnNothing']); } /** * Handle dynamic method calls into the original expectation. * * @param array $arguments * @return Expectation|Expectation|never */ public function __call(string $name, array $arguments): Expectation { try { /* @phpstan-ignore-next-line */ $this->original->{$name}(...$arguments); } catch (ExpectationFailedException) { return $this->original; } $this->throwExpectationFailedException($name, $arguments); } /** * Handle dynamic properties gets into the original expectation. * * @return Expectation|Expectation|never */ public function __get(string $name): Expectation { try { $this->original->{$name}; // @phpstan-ignore-line } catch (ExpectationFailedException) { // @phpstan-ignore-line return $this->original; } $this->throwExpectationFailedException($name); } /** * Creates a new expectation failed exception with a nice readable message. * * @param array|string $arguments */ public function throwExpectationFailedException(string $name, array|string $arguments = []): never { $arguments = is_array($arguments) ? $arguments : [$arguments]; $exporter = new Exporter(); $toString = fn ($argument): string => $exporter->shortenedExport($argument); throw new ExpectationFailedException(sprintf('Expecting %s not %s %s.', $toString($this->original->value), strtolower((string) preg_replace('/(? $toString($argument), $arguments)))); } }