This commit is contained in:
TiclemFR
2023-12-29 17:47:40 +01:00
parent 936d5f15d9
commit 076ec32c3b
565 changed files with 339154 additions and 110 deletions

View File

@@ -0,0 +1,92 @@
<?php
namespace Inertia\Testing;
use Closure;
use const E_USER_DEPRECATED;
use Illuminate\Support\Traits\Macroable;
use PHPUnit\Framework\Assert as PHPUnit;
use Illuminate\Contracts\Support\Arrayable;
use PHPUnit\Framework\AssertionFailedError;
class Assert implements Arrayable
{
use Concerns\Has;
use Concerns\Matching;
use Concerns\Debugging;
use Concerns\PageObject;
use Concerns\Interaction;
use Macroable;
/** @var string */
private $component;
/** @var array */
private $props;
/** @var string */
private $url;
/** @var string|null */
private $version;
/** @var string */
private $path;
protected function __construct(string $component, array $props, string $url, string $version = null, string $path = null)
{
echo "\033[0;31mInertia's built-in 'Assert' library will be removed in a future version of inertia-laravel:\033[0m\n";
echo "\033[0;31m - If you are seeing this error while using \$response->assertInertia(...), please upgrade to Laravel 8.32.0 or higher.\033[0m\n";
echo "\033[0;31m - If you are using the 'Assert' class directly, please adapt your tests to use the 'AssertableInertia' class instead.\033[0m\n";
echo "\033[0;31mFor more information and questions, please see https://github.com/inertiajs/inertia-laravel/pull/338 \033[0m\n\n";
@trigger_error("Inertia's built-in 'Assert' library will be removed in a future version of inertia-laravel: https://github.com/inertiajs/inertia-laravel/pull/338", E_USER_DEPRECATED);
$this->path = $path;
$this->component = $component;
$this->props = $props;
$this->url = $url;
$this->version = $version;
}
protected function dotPath(string $key): string
{
if (is_null($this->path)) {
return $key;
}
return implode('.', [$this->path, $key]);
}
protected function scope(string $key, Closure $callback): self
{
$props = $this->prop($key);
$path = $this->dotPath($key);
PHPUnit::assertIsArray($props, sprintf('Inertia property [%s] is not scopeable.', $path));
$scope = new self($this->component, $props, $this->url, $this->version, $path);
$callback($scope);
$scope->interacted();
return $this;
}
public static function fromTestResponse($response): self
{
try {
$response->assertViewHas('page');
$page = json_decode(json_encode($response->viewData('page')), true);
PHPUnit::assertIsArray($page);
PHPUnit::assertArrayHasKey('component', $page);
PHPUnit::assertArrayHasKey('props', $page);
PHPUnit::assertArrayHasKey('url', $page);
PHPUnit::assertArrayHasKey('version', $page);
} catch (AssertionFailedError $e) {
PHPUnit::fail('Not a valid Inertia response.');
}
return new self($page['component'], $page['props'], $page['url'], $page['version']);
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Inertia\Testing;
use InvalidArgumentException;
use Illuminate\Testing\TestResponse;
use PHPUnit\Framework\Assert as PHPUnit;
use PHPUnit\Framework\AssertionFailedError;
use Illuminate\Testing\Fluent\AssertableJson;
class AssertableInertia extends AssertableJson
{
/** @var string */
private $component;
/** @var string */
private $url;
/** @var string|null */
private $version;
public static function fromTestResponse(TestResponse $response): self
{
try {
$response->assertViewHas('page');
$page = json_decode(json_encode($response->viewData('page')), true);
PHPUnit::assertIsArray($page);
PHPUnit::assertArrayHasKey('component', $page);
PHPUnit::assertArrayHasKey('props', $page);
PHPUnit::assertArrayHasKey('url', $page);
PHPUnit::assertArrayHasKey('version', $page);
} catch (AssertionFailedError $e) {
PHPUnit::fail('Not a valid Inertia response.');
}
$instance = static::fromArray($page['props']);
$instance->component = $page['component'];
$instance->url = $page['url'];
$instance->version = $page['version'];
return $instance;
}
public function component(string $value = null, $shouldExist = null): self
{
PHPUnit::assertSame($value, $this->component, 'Unexpected Inertia page component.');
if ($shouldExist || (is_null($shouldExist) && config('inertia.testing.ensure_pages_exist', true))) {
try {
app('inertia.testing.view-finder')->find($value);
} catch (InvalidArgumentException $exception) {
PHPUnit::fail(sprintf('Inertia page component file [%s] does not exist.', $value));
}
}
return $this;
}
public function url(string $value): self
{
PHPUnit::assertSame($value, $this->url, 'Unexpected Inertia page url.');
return $this;
}
public function version(string $value): self
{
PHPUnit::assertSame($value, $this->version, 'Unexpected Inertia asset version.');
return $this;
}
public function toArray()
{
return [
'component' => $this->component,
'props' => $this->prop(),
'url' => $this->url,
'version' => $this->version,
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Inertia\Testing\Concerns;
trait Debugging
{
public function dump(string $prop = null): self
{
dump($this->prop($prop));
return $this;
}
public function dd(string $prop = null): void
{
dd($this->prop($prop));
}
abstract protected function prop(string $key = null);
}

View File

@@ -0,0 +1,118 @@
<?php
namespace Inertia\Testing\Concerns;
use Closure;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use PHPUnit\Framework\Assert as PHPUnit;
trait Has
{
protected function count(string $key, int $length): self
{
PHPUnit::assertCount(
$length,
$this->prop($key),
sprintf('Inertia property [%s] does not have the expected size.', $this->dotPath($key))
);
return $this;
}
public function hasAll($key): self
{
$keys = is_array($key) ? $key : func_get_args();
foreach ($keys as $prop => $count) {
if (is_int($prop)) {
$this->has($count);
} else {
$this->has($prop, $count);
}
}
return $this;
}
/**
* @param mixed $value
*
* @return $this
*/
public function has(string $key, $value = null, Closure $scope = null): self
{
PHPUnit::assertTrue(
Arr::has($this->prop(), $key),
sprintf('Inertia property [%s] does not exist.', $this->dotPath($key))
);
$this->interactsWith($key);
if (is_int($value) && ! is_null($scope)) {
$path = $this->dotPath($key);
$prop = $this->prop($key);
if ($prop instanceof Collection) {
$prop = $prop->all();
}
PHPUnit::assertTrue($value > 0, sprintf('Cannot scope directly onto the first entry of property [%s] when asserting that it has a size of 0.', $path));
PHPUnit::assertIsArray($prop, sprintf('Direct scoping is currently unsupported for non-array like properties such as [%s].', $path));
$this->count($key, $value);
return $this->scope($key.'.'.array_keys($prop)[0], $scope);
}
if (is_callable($value)) {
$this->scope($key, $value);
} elseif (! is_null($value)) {
$this->count($key, $value);
}
return $this;
}
public function missingAll($key): self
{
$keys = is_array($key) ? $key : func_get_args();
foreach ($keys as $prop) {
$this->misses($prop);
}
return $this;
}
public function missing(string $key): self
{
$this->interactsWith($key);
PHPUnit::assertNotTrue(
Arr::has($this->prop(), $key),
sprintf('Inertia property [%s] was found while it was expected to be missing.', $this->dotPath($key))
);
return $this;
}
public function missesAll($key): self
{
return $this->missingAll(
is_array($key) ? $key : func_get_args()
);
}
public function misses(string $key): self
{
return $this->missing($key);
}
abstract protected function prop(string $key = null);
abstract protected function dotPath(string $key): string;
abstract protected function interactsWith(string $key): void;
abstract protected function scope(string $key, Closure $callback);
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Inertia\Testing\Concerns;
use Illuminate\Support\Str;
use PHPUnit\Framework\Assert as PHPUnit;
trait Interaction
{
/** @var array */
protected $interacted = [];
protected function interactsWith(string $key): void
{
$prop = Str::before($key, '.');
if (! in_array($prop, $this->interacted, true)) {
$this->interacted[] = $prop;
}
}
public function interacted(): void
{
PHPUnit::assertSame(
[],
array_diff(array_keys($this->prop()), $this->interacted),
$this->path
? sprintf('Unexpected Inertia properties were found in scope [%s].', $this->path)
: 'Unexpected Inertia properties were found on the root level.'
);
}
public function etc(): self
{
$this->interacted = array_keys($this->prop());
return $this;
}
abstract protected function prop(string $key = null);
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Inertia\Testing\Concerns;
use Closure;
use Illuminate\Support\Collection;
use PHPUnit\Framework\Assert as PHPUnit;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceResponse;
trait Matching
{
public function whereAll(array $bindings): self
{
foreach ($bindings as $key => $value) {
$this->where($key, $value);
}
return $this;
}
public function where(string $key, $expected): self
{
$this->has($key);
$actual = $this->prop($key);
if ($expected instanceof Closure) {
PHPUnit::assertTrue(
$expected(is_array($actual) ? Collection::make($actual) : $actual),
sprintf('Inertia property [%s] was marked as invalid using a closure.', $this->dotPath($key))
);
return $this;
}
if ($expected instanceof Arrayable) {
$expected = $expected->toArray();
} elseif ($expected instanceof ResourceResponse || $expected instanceof JsonResource) {
$expected = json_decode(json_encode($expected->toResponse(request())->getData()), true);
}
$this->ensureSorted($expected);
$this->ensureSorted($actual);
PHPUnit::assertSame(
$expected,
$actual,
sprintf('Inertia property [%s] does not match the expected value.', $this->dotPath($key))
);
return $this;
}
protected function ensureSorted(&$value): void
{
if (! is_array($value)) {
return;
}
foreach ($value as &$arg) {
$this->ensureSorted($arg);
}
ksort($value);
}
abstract protected function dotPath(string $key): string;
abstract protected function prop(string $key = null);
abstract public function has(string $key, $value = null, Closure $scope = null);
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Inertia\Testing\Concerns;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use PHPUnit\Framework\Assert as PHPUnit;
trait PageObject
{
public function component(string $value = null, $shouldExist = null): self
{
PHPUnit::assertSame($value, $this->component, 'Unexpected Inertia page component.');
if ($shouldExist || (is_null($shouldExist) && config('inertia.testing.ensure_pages_exist', true))) {
try {
app('inertia.testing.view-finder')->find($value);
} catch (InvalidArgumentException $exception) {
PHPUnit::fail(sprintf('Inertia page component file [%s] does not exist.', $value));
}
}
return $this;
}
protected function prop(string $key = null)
{
return Arr::get($this->props, $key);
}
public function url(string $value): self
{
PHPUnit::assertSame($value, $this->url, 'Unexpected Inertia page url.');
return $this;
}
public function version(string $value): self
{
PHPUnit::assertSame($value, $this->version, 'Unexpected Inertia asset version.');
return $this;
}
public function toArray(): array
{
return [
'component' => $this->component,
'props' => $this->props,
'url' => $this->url,
'version' => $this->version,
];
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Inertia\Testing;
use Closure;
use Illuminate\Testing\Fluent\AssertableJson;
class TestResponseMacros
{
public function assertInertia()
{
return function (Closure $callback = null) {
if (class_exists(AssertableJson::class)) {
$assert = AssertableInertia::fromTestResponse($this);
} else {
$assert = Assert::fromTestResponse($this);
}
if (is_null($callback)) {
return $this;
}
$callback($assert);
return $this;
};
}
public function inertiaPage()
{
return function () {
if (class_exists(AssertableJson::class)) {
return AssertableInertia::fromTestResponse($this)->toArray();
}
return Assert::fromTestResponse($this)->toArray();
};
}
}