Laragear/Alerts

View on GitHub
src/Testing/Builder.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Laragear\Alerts\Testing;

use Countable;
use Illuminate\Support\Collection;
use Illuminate\Testing\Assert as PHPUnit;
use function in_array;
use function is_array;
use function is_string;
use JetBrains\PhpStorm\Pure;
use Laragear\Alerts\Alert;
use Laragear\Alerts\Testing\Fakes\BagFake;
use function sort;
use function strcmp;
use function trans;
use function trans_choice;
use function url;
use function usort;

class Builder
{
    /**
     * Create a new expectation builder instance.
     *
     * @param  \Laragear\Alerts\Testing\Fakes\BagFake  $bag
     * @param  string|null  $message
     * @param  string[]|null  $types
     * @param  bool|null  $dismiss
     * @param  string[]|bool|null|string  $persisted
     * @param  string[]|null  $tags
     * @param  string[]|null  $links
     * @param  bool  $anyTag
     */
    public function __construct(
        public BagFake $bag,
        protected ?string $message = null,
        protected ?array $types = null,
        protected ?bool $dismiss = null,
        protected array|bool|null|string $persisted = null,
        protected ?array $tags = null,
        protected ?array $links = null,
        protected bool $anyTag = false,
    ) {
        //
    }

    /**
     * Expect an alert with the raw message.
     *
     * @param  string  $message
     * @return $this
     */
    public function withRaw(string $message): static
    {
        $this->message = $message;

        return $this;
    }

    /**
     * Expect an alert with the message.
     *
     * @param  string  $message
     * @return $this
     */
    public function withMessage(string $message): static
    {
        return $this->withRaw(e($message));
    }

    /**
     * Expect an alert with a translated message.
     *
     * @param  string  $key
     * @param  array  $replace
     * @param  string|null  $locale
     * @return $this
     */
    public function withTrans(string $key, array $replace = [], string $locale = null): static
    {
        return $this->withRaw(trans($key, $replace, $locale));
    }

    /**
     * Expect an alert with a translated (choice) message.
     *
     * @param  string  $key
     * @param  Countable|int|array  $number
     * @param  array  $replace
     * @param  string|null  $locale
     * @return $this
     */
    public function withTransChoice(
        string $key,
        Countable|int|array $number,
        array $replace = [],
        string $locale = null
    ): static {
        return $this->withRaw(trans_choice($key, $number, $replace, $locale));
    }

    /**
     * Expect an alert with a link away.
     *
     * @param  string  $replace
     * @param  string  $url
     * @param  bool  $blank
     * @return $this
     */
    public function withAway(string $replace, string $url, bool $blank = true): static
    {
        $this->links[] = (object) [
            'replace' => $replace,
            'url'     => $url,
            'blank'   => $blank,
        ];

        usort($this->links, static function (object $first, object $second): int {
            return strcmp($first->replace.$first->url, $second->replace.$second->url);
        });

        return $this;
    }

    /**
     * Expect an alert with a link to a path.
     *
     * @param  string  $replace
     * @param  string  $url
     * @param  bool  $blank
     * @return $this
     */
    public function withTo(string $replace, string $url, bool $blank = false): static
    {
        return $this->withAway($replace, url($url), $blank);
    }

    /**
     * Expect an alert with a link to a route.
     *
     * @param  string  $replace
     * @param  string  $name
     * @param  array  $parameters
     * @param  bool  $blank
     * @return $this
     */
    public function withRoute(string $replace, string $name, array $parameters = [], bool $blank = false): static
    {
        return $this->withAway($replace, url()->route($name, $parameters), $blank);
    }

    /**
     * Expect an alert with a link to an action.
     *
     * @param  string  $replace
     * @param  string|array  $action
     * @param  array  $parameters
     * @param  bool  $blank
     * @return $this
     */
    public function withAction(string $replace, string|array $action, array $parameters = [], bool $blank = false): static
    {
        return $this->withAway($replace, url()->action($action, $parameters), $blank);
    }

    /**
     * Expect an alert with the issued types.
     *
     * @param  string  ...$types
     * @return $this
     */
    public function withTypes(string ...$types): static
    {
        $this->types = $types;

        sort($this->types);

        return $this;
    }

    /**
     * Expect an alert persisted.
     *
     * @return $this
     */
    public function persisted(): static
    {
        $this->persisted = true;

        return $this;
    }

    /**
     * Expect an alert not persisted.
     *
     * @return $this
     */
    public function notPersisted(): static
    {
        $this->persisted = false;

        return $this;
    }

    /**
     * Expect an alert dismissible.
     *
     * @return $this
     */
    public function dismissible(): static
    {
        $this->dismiss = true;

        return $this;
    }

    /**
     * Expect an alert not dismissible.
     *
     * @return $this
     */
    public function notDismissible(): static
    {
        $this->dismiss = false;

        return $this;
    }

    /**
     * Expect an alert with the given tags.
     *
     * @param  string  ...$tags
     * @return $this
     */
    public function withTag(string ...$tags): static
    {
        $this->tags = $tags;

        return $this;
    }

    /**
     * Expect an alert with any of the given tags.
     *
     * @param  string  ...$tags
     * @return $this
     */
    public function withAnyTag(string ...$tags): static
    {
        $this->anyTag = true;

        return $this->withTag(...$tags);
    }

    /**
     * Returns a collection of all matching alerts.
     *
     * @return \Illuminate\Support\Collection<int, \Laragear\Alerts\Alert>
     */
    protected function matches(): Collection
    {
        return $this->bag->added->filter(function (Alert $alert): bool {
            return $this->is($alert);
        });
    }

    /**
     * Check if the given alert matches the expectations.
     *
     * @param  \Laragear\Alerts\Alert  $alert
     * @return bool
     */
    #[Pure]
    protected function is(Alert $alert): bool
    {
        if ($this->message !== null && $this->message !== $alert->getMessage()) {
            return false;
        }

        if ($this->dismiss !== null && $this->dismiss !== $alert->isDismissible()) {
            return false;
        }

        if ($this->types !== null && $this->types !== $alert->getTypes()) {
            return false;
        }

        if ($this->tags !== null) {
            if ($this->anyTag) {
                return $alert->hasAnyTag(...$this->tags);
            }

            return $this->tags === $alert->getTags();
        }

        if ($this->persisted !== null) {
            if (is_string($this->persisted)) {
                return $this->persisted === $alert->getPersistKey();
            }

            if (is_array($this->persisted)) {
                return in_array($alert->getPersistKey(), $this->persisted, true);
            }

            return $this->persisted === (bool) $alert->getPersistKey();
        }

        if ($this->links !== null && $this->links != $alert->getLinks()) {
            return false;
        }

        return true;
    }

    /**
     * Expect an alert persisted with the issued key.
     *
     * @param  string  ...$key
     * @return void
     */
    public function persistedAs(string ...$key): void
    {
        $this->persisted = $key;

        $count = count($key);

        $this->count($count, "Failed to assert that [$count] persistent alerts exist.");
    }

    /**
     * Assert that at least one Alert exists with the given expectations.
     *
     * @param  string  $message
     * @return void
     */
    public function exists(string $message = 'Failed to assert that at least one alert matches the expectations.'): void
    {
        PHPUnit::assertNotEmpty($this->matches(), $message);
    }

    /**
     * Assert that no Alert exists with the given expectations.
     *
     * @param  string  $message
     * @return void
     */
    public function missing(string $message = 'Failed to assert that no alert matches the expectations.'): void
    {
        PHPUnit::assertEmpty($this->matches(), $message);
    }

    /**
     * Assert that only one Alert exists with the given expectations.
     *
     * @param  string  $message
     * @return void
     */
    public function unique(string $message = 'Failed to assert that there is only one alert.'): void
    {
        $this->count(1, $message);
    }

    /**
     * Assert that the given number of Alerts matches exactly the given expectations.
     *
     * @param  int  $count
     * @param  string|null  $message
     * @return void
     */
    public function count(int $count, string $message = null): void
    {
        $matches = $this->matches();

        PHPUnit::assertCount(
            $count, $matches,
            $message ?? "Failed to assert that [{$matches->count()}] alerts match the expected [$count] count."
        );
    }
}