diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index e3a9a756..8c8898ca 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -30,7 +30,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: mbstring, fileinfo + extensions: mbstring, fileinfo, pdo_sqlite coverage: none tools: composer:v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe86c9c..3ba6a141 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `laravel-ignition` will be documented in this file +## 2.6.0 - 2024-04-29 + +### What's Changed + +* Livewire 3 by @timvandijck en @riasvdv in https://github.com/spatie/laravel-ignition/pull/193 + ## 2.5.2 - 2024-04-16 ### What's Changed diff --git a/composer.json b/composer.json index 709455db..f0e9c7c5 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "ext-mbstring": "*", "illuminate/support": "^10.0|^11.0", "spatie/flare-client-php": "^1.3.5", - "spatie/ignition": "^1.13.2", + "spatie/ignition": "^1.14", "symfony/console": "^6.2.3|^7.0", "symfony/var-dumper": "^6.2.3|^7.0" }, @@ -31,11 +31,11 @@ "livewire/livewire": "^2.11|^3.3.5", "mockery/mockery": "^1.5.1", "openai-php/client": "^0.8.1", - "orchestra/testbench": "^8.0|^9.0", - "pestphp/pest": "^2.30", - "phpstan/extension-installer": "^1.2", + "orchestra/testbench": "8.22.3|^9.0", + "pestphp/pest": "^2.34", + "phpstan/extension-installer": "^1.3.1", "phpstan/phpstan-deprecation-rules": "^1.1.1", - "phpstan/phpstan-phpunit": "^1.3.3", + "phpstan/phpstan-phpunit": "^1.3.16", "vlucas/phpdotenv": "^5.5" }, "suggest": { diff --git a/src/ContextProviders/LaravelLivewireRequestContextProvider.php b/src/ContextProviders/LaravelLivewireRequestContextProvider.php index e421e118..7a73d6e7 100644 --- a/src/ContextProviders/LaravelLivewireRequestContextProvider.php +++ b/src/ContextProviders/LaravelLivewireRequestContextProvider.php @@ -40,6 +40,26 @@ public function toArray(): array /** @return array */ protected function getLivewireInformation(): array { + if ($this->request->has('components')) { + $data = []; + + foreach ($this->request->get('components') as $component) { + $snapshot = json_decode($component['snapshot'], true); + + $class = app(ComponentRegistry::class)->getClass($snapshot['memo']['name']); + + $data[] = [ + 'component_class' => $class ?? null, + 'data' => $snapshot['data'], + 'memo' => $snapshot['memo'], + 'updates' => $this->resolveUpdates($component['updates']), + 'calls' => $component['calls'], + ]; + } + + return $data; + } + /** @phpstan-ignore-next-line */ $componentId = $this->request->input('fingerprint.id'); @@ -56,12 +76,20 @@ protected function getLivewireInformation(): array $componentClass = null; } + /** @phpstan-ignore-next-line */ + $updates = $this->request->input('updates') ?? []; + + /** @phpstan-ignore-next-line */ + $updates = $this->request->input('updates') ?? []; + return [ - 'component_class' => $componentClass, - 'component_alias' => $componentAlias, - 'component_id' => $componentId, - 'data' => $this->resolveData(), - 'updates' => $this->resolveUpdates(), + [ + 'component_class' => $componentClass, + 'component_alias' => $componentAlias, + 'component_id' => $componentId, + 'data' => $this->resolveData(), + 'updates' => $this->resolveUpdates($updates), + ], ]; } @@ -86,7 +114,7 @@ protected function resolveData(): array } /** @return array */ - protected function resolveUpdates(): array + protected function resolveUpdates(array $updates): array { /** @phpstan-ignore-next-line */ $updates = $this->request->input('updates') ?? []; diff --git a/tests/ContextProviders/LaravelLivewireRequestContextProviderTest.php b/tests/ContextProviders/LaravelLivewireRequestContextProviderTest.php index 2cf7fc1d..5e0137fd 100644 --- a/tests/ContextProviders/LaravelLivewireRequestContextProviderTest.php +++ b/tests/ContextProviders/LaravelLivewireRequestContextProviderTest.php @@ -6,8 +6,8 @@ use Spatie\LaravelIgnition\Tests\TestClasses\FakeLivewireManager; beforeEach(function () { - $this->livewireManager = FakeLivewireManager::setUp(); -})->skip(LIVEWIRE_VERSION_3, 'Missing Livewire 3 support.'); + $this->livewireManager = resolve(FakeLivewireManager::class); +})->skip(LIVEWIRE_VERSION_2, 'Only test Livewire 3.'); it('returns the referer url and method', function () { $context = createRequestContext([ @@ -17,15 +17,15 @@ $request = $context->getRequest(); - expect($request['url'])->toBe('http://localhost/referred'); - expect($request['method'])->toBe('GET'); + expect($request['url'])->toBe('http://localhost/POST'); + expect($request['method'])->toBe('POST'); }); it('returns livewire component information', function () { $alias = 'fake-component'; $class = 'fake-class'; - $this->livewireManager->fakeAliases[$alias] = $class; + $this->livewireManager->addAlias($alias, $class); $context = createRequestContext([ 'path' => 'http://localhost/referred', @@ -36,9 +36,8 @@ $livewire = $context->toArray()['livewire']; - expect($livewire['component_id'])->toBe($id); - expect($livewire['component_alias'])->toBe($alias); - expect($livewire['component_class'])->toBe($class); + expect($livewire[0]['component_id'])->toBe($id); + expect($livewire[0]['component_alias'])->toBe($alias); }); it('returns livewire component information when it does not exist', function () { @@ -51,9 +50,9 @@ $livewire = $context->toArray()['livewire']; - expect($livewire['component_id'])->toBe($id); - expect($livewire['component_alias'])->toBe($name); - expect($livewire['component_class'])->toBeNull(); + expect($livewire[0]['component_id'])->toBe($id); + expect($livewire[0]['component_alias'])->toBe($name); + expect($livewire[0]['component_class'])->toBeNull(); }); it('removes ids from update payloads', function () { @@ -75,9 +74,9 @@ $livewire = $context->toArray()['livewire']; - expect($livewire['component_id'])->toBe($id); - expect($livewire['component_alias'])->toBe($name); - expect($livewire['component_class'])->toBeNull(); + expect($livewire[0]['component_id'])->toBe($id); + expect($livewire[0]['component_alias'])->toBe($name); + expect($livewire[0]['component_class'])->toBeNull(); }); it('combines data into one payload', function () { @@ -150,7 +149,7 @@ "collection" => ['a', 'b'], "stringable" => "Test", "wireable" => ['a', 'b'], - ], $livewire['data']); + ], $livewire[0]['data']); }); // Helpers diff --git a/tests/ContextProviders/LegacyLivewireRequestContextProviderTest.php b/tests/ContextProviders/LegacyLivewireRequestContextProviderTest.php new file mode 100644 index 00000000..8c1c958b --- /dev/null +++ b/tests/ContextProviders/LegacyLivewireRequestContextProviderTest.php @@ -0,0 +1,172 @@ +livewireManager = FakeLivewireManager::setUp(); +})->skip(LIVEWIRE_VERSION_3, 'Only test Livewire 2.'); + +it('returns the referer url and method', function () { + $context = createLegacyRequestContext([ + 'path' => 'referred', + 'method' => 'GET', + ]); + + $request = $context->getRequest(); + + expect($request['url'])->toBe('http://localhost/referred'); + expect($request['method'])->toBe('GET'); +}); + +it('returns livewire component information', function () { + $alias = 'fake-component'; + $class = 'fake-class'; + + $this->livewireManager->fakeAliases[$alias] = $class; + + $context = createLegacyRequestContext([ + 'path' => 'http://localhost/referred', + 'method' => 'GET', + 'id' => $id = uniqid(), + 'name' => $alias, + ]); + + $livewire = $context->toArray()['livewire']; + + expect($livewire[0]['component_id'])->toBe($id); + expect($livewire[0]['component_alias'])->toBe($alias); + expect($livewire[0]['component_class'])->toBe($class); +}); + +it('returns livewire component information when it does not exist', function () { + $context = createLegacyRequestContext([ + 'path' => 'http://localhost/referred', + 'method' => 'GET', + 'id' => $id = uniqid(), + 'name' => $name = 'fake-component', + ]); + + $livewire = $context->toArray()['livewire']; + + expect($livewire[0]['component_id'])->toBe($id); + expect($livewire[0]['component_alias'])->toBe($name); + expect($livewire[0]['component_class'])->toBeNull(); +}); + +it('removes ids from update payloads', function () { + $context = createLegacyRequestContext([ + 'path' => 'http://localhost/referred', + 'method' => 'GET', + 'id' => $id = uniqid(), + 'name' => $name = 'fake-component', + ], [ + [ + 'type' => 'callMethod', + 'payload' => [ + 'id' => 'remove-me', + 'method' => 'chang', + 'params' => ['a'], + ], + ], + ]); + + $livewire = $context->toArray()['livewire']; + + expect($livewire[0]['component_id'])->toBe($id); + expect($livewire[0]['component_alias'])->toBe($name); + expect($livewire[0]['component_class'])->toBeNull(); +}); + +it('combines data into one payload', function () { + $context = createLegacyRequestContext([ + 'path' => 'http://localhost/referred', + 'method' => 'GET', + 'id' => uniqid(), + 'name' => 'fake-component', + ], [], [ + 'data' => [ + 'string' => 'Ruben', + 'array' => ['a', 'b'], + 'modelCollection' => [], + 'model' => [], + 'date' => '2021-11-10T14:20:36+0000', + 'collection' => ['a', 'b'], + 'stringable' => 'Test', + 'wireable' => ['a', 'b'], + ], + 'dataMeta' => [ + 'modelCollections' => [ + 'modelCollection' => [ + 'class' => 'App\\\\Models\\\\User', + 'id' => [1, 2, 3, 4], + 'relations' => [], + 'connection' => 'mysql', + ], + ], + 'models' => [ + 'model' => [ + 'class' => 'App\\\\Models\\\\User', + 'id' => 1, + 'relations' => [], + 'connection' => 'mysql', + ], + ], + 'dates' => [ + 'date' => 'carbonImmutable', + ], + 'collections' => [ + 'collection', + ], + 'stringables' => [ + 'stringable', + ], + 'wireables' => [ + 'wireable', + ], + ], + ]); + + $livewire = $context->toArray()['livewire']; + + $this->assertEquals([ + "string" => "Ruben", + "array" => ['a', 'b'], + "modelCollection" => [ + "class" => "App\\\\Models\\\\User", + "id" => [1, 2, 3, 4], + "relations" => [], + "connection" => "mysql", + ], + "model" => [ + "class" => "App\\\\Models\\\\User", + "id" => 1, + "relations" => [], + "connection" => "mysql", + ], + "date" => "2021-11-10T14:20:36+0000", + "collection" => ['a', 'b'], + "stringable" => "Test", + "wireable" => ['a', 'b'], + ], $livewire[0]['data']); +}); + +// Helpers +function createLegacyRequestContext(array $fingerprint, array $updates = [], array $serverMemo = []): LaravelLivewireRequestContextProvider +{ + $providedRequest = null; + + Route::post('livewire', function (Request $request) use (&$providedRequest) { + $providedRequest = $request; + })->name('livewire.message'); + + test()->postJson('livewire', [ + 'fingerprint' => $fingerprint, + 'serverMemo' => $serverMemo, + 'updates' => $updates, + ], ['X-Livewire' => 1]); + + return new LaravelLivewireRequestContextProvider($providedRequest, test()->livewireManager); +} diff --git a/tests/Pest.php b/tests/Pest.php index 2166e3e2..10946295 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -4,6 +4,7 @@ use Livewire\Mechanisms\ComponentRegistry; use Spatie\LaravelIgnition\Tests\TestCase; +define('LIVEWIRE_VERSION_2', ! class_exists(ComponentRegistry::class)); define('LIVEWIRE_VERSION_3', class_exists(ComponentRegistry::class)); uses(TestCase::class)->in(__DIR__); diff --git a/tests/TestClasses/FakeLivewireManager.php b/tests/TestClasses/FakeLivewireManager.php index 394ae3f4..11344a49 100644 --- a/tests/TestClasses/FakeLivewireManager.php +++ b/tests/TestClasses/FakeLivewireManager.php @@ -3,6 +3,7 @@ namespace Spatie\LaravelIgnition\Tests\TestClasses; use Livewire\LivewireManager; +use Livewire\Mechanisms\ComponentRegistry; class FakeLivewireManager extends LivewireManager { @@ -24,7 +25,7 @@ public function isDefinitelyLivewireRequest() public function getClass($alias) { - return $this->fakeAliases[$alias] ?? parent::getClass($alias); + return $this->fakeAliases[$alias] ?? app(ComponentRegistry::class)->getClass($alias); } public function addAlias(string $alias, string $class): void