Skip to content

Commit c220384

Browse files
authored
Add support for Livewire 4 (#45)
* fix: add return types * bump: update livewire v3 to v4 * fix(livewire): use Finder instead of deprecated ComponentRegistry * fix(tests): set array cache driver to avoid test failures * fix(assertions): update wire:model regex to support Livewire 4 modifiers\ Replace deprecated modifiers (defer, lazy-with-duration) with Livewire 4 equivalents and add support for new modifiers: deep, number, and fill. Also escape property names with preg_quote for special character safety.
1 parent 890c46c commit c220384

10 files changed

Lines changed: 47 additions & 37 deletions

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
},
2424
"require-dev": {
2525
"brianium/paratest": "^6.2|^7.4",
26-
"livewire/livewire": "^3.0",
26+
"livewire/livewire": "^4.0",
2727
"orchestra/testbench": "^8.0|^7.4|^9.0|^10.0",
2828
"pestphp/pest": "^2.34|^3.7",
2929
"phpunit/phpunit": "^9.3|^10.5|^11.5.3",

src/CustomLivewireAssertionsMixin.php

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use Closure;
66
use Illuminate\Support\Str;
77
use Livewire\Component;
8-
use Livewire\Mechanisms\ComponentRegistry;
8+
use Livewire\Finder\Finder;
99
use PHPUnit\Framework\Assert as PHPUnit;
1010

1111
/**
@@ -17,7 +17,7 @@ public function assertPropertyWired(): Closure
1717
{
1818
return function (string $property) {
1919
PHPUnit::assertMatchesRegularExpression(
20-
'/wire:model(\.(defer|live|blur|change|boolean|self|(lazy|debounce)(\.\d+?(ms|s)|)))*=(?<q>"|\')'.$property.'(\k\'q\')/',
20+
'/wire:model(?:\.live(?:\.debounce(?:\.\d+(?:ms|s))?)?|\.(?:blur|lazy|change|boolean|self|deep|number|fill))*=(?<q>"|\')'.preg_quote($property, '/').'\k\'q\'/',
2121
$this->html()
2222
);
2323

@@ -32,7 +32,7 @@ public function assertPropertyNotWired(): Closure
3232
{
3333
return function (string $property) {
3434
PHPUnit::assertDoesNotMatchRegularExpression(
35-
'/wire:model(\.(live|blur|change|boolean|self|(lazy|debounce)(\.\d+?(ms|s)|)))*=(?<q>"|\')'.$property.'(\k\'q\')/',
35+
'/wire:model(?:\.live(?:\.debounce(?:\.\d+(?:ms|s))?)?|\.(?:blur|lazy|change|boolean|self|deep|number|fill))*=(?<q>"|\')'.preg_quote($property, '/').'\k\'q\'/',
3636
$this->html()
3737
);
3838

@@ -56,7 +56,7 @@ public function assertPropertyEntangled(): Closure
5656
. preg_quote("'" . $property . "'")
5757
. ')';
5858
PHPUnit::assertMatchesRegularExpression(
59-
'/(.|\$wire\.)entangle\('.$propertyRe.'\)/',
59+
'/(.|\$wire\.)entangle\(' . $propertyRe . '\)/',
6060
$this->html()
6161
);
6262

@@ -80,7 +80,7 @@ public function assertPropertyNotEntangled(): Closure
8080
. preg_quote("'" . $property . "'")
8181
. ')';
8282
PHPUnit::assertDoesNotMatchRegularExpression(
83-
'/(.|\$wire\.)entangle\('.$propertyRe.'\)/',
83+
'/(.|\$wire\.)entangle\(' . $propertyRe . '\)/',
8484
$this->html()
8585
);
8686

@@ -95,7 +95,7 @@ public function assertMethodWired(): Closure
9595
{
9696
return function (string $method) {
9797
PHPUnit::assertMatchesRegularExpression(
98-
'/wire:click(\.(prevent))?=(?<q>"|\')'.preg_quote($method).'(\s*\(.+\)\s*)?\s*(\k\'q\')/',
98+
'/wire:click(\.(prevent))?=(?<q>"|\')' . preg_quote($method) . '(\s*\(.+\)\s*)?\s*(\k\'q\')/',
9999
$this->html()
100100
);
101101

@@ -110,7 +110,7 @@ public function assertMethodNotWired(): Closure
110110
{
111111
return function (string $method) {
112112
PHPUnit::assertDoesNotMatchRegularExpression(
113-
'/wire:click(\.(prevent))?=(?<q>"|\')'.preg_quote($method).'(\s*\(.+\)\s*)?\s*(\k\'q\')/',
113+
'/wire:click(\.(prevent))?=(?<q>"|\')' . preg_quote($method) . '(\s*\(.+\)\s*)?\s*(\k\'q\')/',
114114
$this->html()
115115
);
116116

@@ -125,7 +125,7 @@ public function assertMethodWiredToAction(): Closure
125125
{
126126
return function (string $action, string $methodName) {
127127
PHPUnit::assertMatchesRegularExpression(
128-
'/wire:' . $action . '?=(?<q>"|\')'.preg_quote($methodName).'(\s*\(.+\)\s*)?\s*(\k\'q\')/',
128+
'/wire:' . $action . '?=(?<q>"|\')' . preg_quote($methodName) . '(\s*\(.+\)\s*)?\s*(\k\'q\')/',
129129
$this->html()
130130
);
131131

@@ -140,7 +140,7 @@ public function assertMethodNotWiredToAction(): Closure
140140
{
141141
return function (string $action, string $methodName) {
142142
PHPUnit::assertDoesNotMatchRegularExpression(
143-
'/wire:' . $action . '?=(?<q>"|\')'.preg_quote($methodName).'(\s*\(.+\)\s*)?\s*(\k\'q\')/',
143+
'/wire:' . $action . '?=(?<q>"|\')' . preg_quote($methodName) . '(\s*\(.+\)\s*)?\s*(\k\'q\')/',
144144
$this->html()
145145
);
146146

@@ -155,7 +155,7 @@ public function assertMethodWiredToForm(): Closure
155155
{
156156
return function (string $method) {
157157
PHPUnit::assertMatchesRegularExpression(
158-
'/wire:submit(\.(prevent))*=(?<q>"|\')'.$method.'(\k\'q\')/',
158+
'/wire:submit(\.(prevent))*=(?<q>"|\')' . $method . '(\k\'q\')/',
159159
$this->html()
160160
);
161161

@@ -170,7 +170,7 @@ public function assertMethodNotWiredToForm(): Closure
170170
{
171171
return function (string $method) {
172172
PHPUnit::assertDoesNotMatchRegularExpression(
173-
'/wire:submit(\.(prevent))*=(?<q>"|\')'.$method.'(\k\'q\')/',
173+
'/wire:submit(\.(prevent))*=(?<q>"|\')' . $method . '(\k\'q\')/',
174174
$this->html()
175175
);
176176

@@ -185,7 +185,7 @@ public function assertMethodWiredToEvent(): Closure
185185
{
186186
return function (string $method, string $event) {
187187
PHPUnit::assertMatchesRegularExpression(
188-
'/wire:'.preg_quote($event, '/').'(\.[a-zA-Z0-9\-]+)*=(?<q>"|\')'.$method.'(\s*\(.+\)\s*)?\s*(\k\'q\')/',
188+
'/wire:' . preg_quote($event, '/') . '(\.[a-zA-Z0-9\-]+)*=(?<q>"|\')' . $method . '(\s*\(.+\)\s*)?\s*(\k\'q\')/',
189189
$this->html()
190190
);
191191

@@ -200,7 +200,7 @@ public function assertMethodNotWiredToEvent(): Closure
200200
{
201201
return function (string $method, string $event) {
202202
PHPUnit::assertDoesNotMatchRegularExpression(
203-
'/wire:'.preg_quote($event, '/').'(\.[a-zA-Z0-9\-]+)*=(?<q>"|\')'.$method.'(\s*\(.+\)\s*)?\s*(\k\'q\')/',
203+
'/wire:' . preg_quote($event, '/') . '(\.[a-zA-Z0-9\-]+)*=(?<q>"|\')' . $method . '(\s*\(.+\)\s*)?\s*(\k\'q\')/',
204204
$this->html()
205205
);
206206

@@ -215,7 +215,7 @@ public function assertMethodWiredToEventWithoutModifiers(): Closure
215215
{
216216
return function (string $method, string $event) {
217217
PHPUnit::assertMatchesRegularExpression(
218-
'/wire:'.preg_quote($event, '/').'=(?<q>"|\')'.$method.'(\s*\(.+\)\s*)?\s*(\k\'q\')/',
218+
'/wire:' . preg_quote($event, '/') . '=(?<q>"|\')' . $method . '(\s*\(.+\)\s*)?\s*(\k\'q\')/',
219219
$this->html()
220220
);
221221

@@ -230,7 +230,7 @@ public function assertMethodNotWiredToEventWithoutModifiers(): Closure
230230
{
231231
return function (string $method, string $event) {
232232
PHPUnit::assertDoesNotMatchRegularExpression(
233-
'/wire:'.preg_quote($event, '/').'=(?<q>"|\')'.$method.'(\s*\(.+\)\s*)?\s*(\k\'q\')/',
233+
'/wire:' . preg_quote($event, '/') . '=(?<q>"|\')' . $method . '(\s*\(.+\)\s*)?\s*(\k\'q\')/',
234234
$this->html()
235235
);
236236

@@ -245,13 +245,13 @@ public function assertContainsLivewireComponent(): Closure
245245
{
246246
return function (string $component) {
247247
if (is_subclass_of($component, Component::class)) {
248-
$component = app(ComponentRegistry::class)->getName($component);
248+
$component = app(Finder::class)->normalizeName($component);
249249
}
250250

251251
$componentHaystackView = file_get_contents($this->lastState->getView()->getPath());
252252

253253
PHPUnit::assertMatchesRegularExpression(
254-
'/@livewire\(\s*\''.$component.'\'|<livewire\:'.$component.'/',
254+
'/@livewire\(\s*\'' . $component . '\'|<livewire\:' . $component . '/',
255255
$componentHaystackView
256256
);
257257

@@ -266,13 +266,13 @@ public function assertDoesNotContainLivewireComponent(): Closure
266266
{
267267
return function (string $component) {
268268
if (is_subclass_of($component, Component::class)) {
269-
$component = app(ComponentRegistry::class)->getName($component);
269+
$component = app(Finder::class)->normalizeName($component);
270270
}
271271

272272
$componentHaystackView = file_get_contents($this->lastState->getView()->getPath());
273273

274274
PHPUnit::assertDoesNotMatchRegularExpression(
275-
'/@livewire\(\''.$component.'\'|<livewire\:'.$component.'/',
275+
'/@livewire\(\'' . $component . '\'|<livewire\:' . $component . '/',
276276
$componentHaystackView
277277
);
278278

tests/AssertionsTest.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020
->assertPropertyWired('change')
2121
->assertPropertyWired('boolean')
2222
->assertPropertyWired('self')
23-
->assertPropertyWired('defer')
2423
->assertPropertyWired('lazy')
2524
->assertPropertyWired('live')
2625
->assertPropertyWired('debounce')
27-
->assertPropertyWired('lazy-with-duration')
2826
->assertPropertyWired('debounce-with-duration')
29-
->assertPropertyWired('singlequote');
27+
->assertPropertyWired('singlequote')
28+
->assertPropertyWired('deep')
29+
->assertPropertyWired('fill')
30+
->assertPropertyWired('number');
3031
});
3132

3233
it('checks if Livewire property is not wired to a field', function () {
@@ -38,11 +39,12 @@
3839
->assertPropertyNotWired('self_not_wired')
3940
->assertPropertyNotWired('lazy_not_wired')
4041
->assertPropertyNotWired('live_not_wired')
41-
->assertPropertyNotWired('defer_not_wired')
4242
->assertPropertyNotWired('debounce_not_wired')
43-
->assertPropertyNotWired('lazy-with-duration_not_wired')
4443
->assertPropertyNotWired('debounce-with-duration_not_wired')
45-
->assertPropertyNotWired('singlequote_not_wired');
44+
->assertPropertyNotWired('singlequote_not_wired')
45+
->assertPropertyNotWired('deep_not_wired')
46+
->assertPropertyNotWired('fill_not_wired')
47+
->assertPropertyNotWired('number_not_wired');
4648
});
4749

4850
it('checks if Livewire property is entangled to a field', function () {
@@ -305,7 +307,7 @@ function () {
305307

306308
it('checks if it sees string before other string', function () {
307309
Livewire::test(LivewireTestComponentA::class)
308-
->assertSeeBefore('First value', 'Second value');
310+
->assertSeeBefore('First value', 'Second value');
309311
});
310312

311313
it('checks if it does not see string before other string', function () {

tests/Components/FileDownloadComponent.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
namespace Tests\Components;
44

55
use Livewire\Component;
6+
use Symfony\Component\HttpFoundation\StreamedResponse;
67

78
class FileDownloadComponent extends Component
89
{
9-
public function streamDownload($filename = null, $headers = [])
10+
public function streamDownload($filename = null, $headers = []): StreamedResponse
1011
{
1112
return response()->streamDownload(function () {
1213
echo 'alpinejs';
1314
}, $filename, $headers);
1415
}
1516

16-
public function render()
17+
public function render(): string
1718
{
1819
return '<div></div>';
1920
}

tests/Components/LivewireTestComponentA.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
namespace Tests\Components;
44

5+
use Illuminate\Contracts\View\View;
56
use Livewire\Component;
67

78
class LivewireTestComponentA extends Component
89
{
9-
public function render()
10+
public function render(): View
1011
{
1112
return view('livewire-test-component-a');
1213
}

tests/Components/LivewireTestComponentB.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
namespace Tests\Components;
44

5+
use Illuminate\Contracts\View\View;
56
use Livewire\Component;
67

78
class LivewireTestComponentB extends Component
89
{
9-
public function render()
10+
public function render(): View
1011
{
1112
return view('livewire-test-component-b');
1213
}

tests/Components/LivewireTestComponentC.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
namespace Tests\Components;
44

5+
use Illuminate\Contracts\View\View;
56
use Livewire\Component;
67

78
class LivewireTestComponentC extends Component
89
{
9-
public function render()
10+
public function render(): View
1011
{
1112
return view('livewire-test-component-c');
1213
}

tests/Components/LivewireTestComponentD.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
namespace Tests\Components;
44

5+
use Illuminate\Contracts\View\View;
56
use Livewire\Component;
67

78
class LivewireTestComponentD extends Component
89
{
9-
public function render()
10+
public function render(): View
1011
{
1112
return view('livewire-test-component-d');
1213
}

tests/TestCase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function getEnvironmentSetUp($app)
3939
'database' => ':memory:',
4040
'prefix' => '',
4141
]);
42+
$app['config']->set('cache.default', 'array');
4243

4344
/*
4445
include_once __DIR__.'/../database/migrations/create_missing-livewire-assertions_table.php.stub';

tests/resources/views/livewire-test-component-a.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
<div>
22
<input type="text" wire:model="user" />
3+
<input type="text" wire:model="user.email" />
34
<input type="text" wire:model.blur="blur" />
45
<input type="text" wire:model.change="change" />
56
<input type="text" wire:model.boolean="boolean" />
67
<input type="text" wire:model.self="self" />
78
<input type="text" wire:model.lazy="lazy" />
89
<input type="text" wire:model.live="live" />
9-
<input type="text" wire:model.defer="defer" />
10-
<input type="text" wire:model.debounce="debounce" />
11-
<input type="text" wire:model.lazy.200s="lazy-with-duration" />
12-
<input type="text" wire:model.debounce.500ms="debounce-with-duration" />
10+
<input type="text" wire:model.live.debounce="debounce" />
11+
<input type="text" wire:model.live.debounce.500ms="debounce-with-duration" />
1312
<input type="text" wire:model='singlequote' />
13+
<input type="text" wire:model.deep="deep" />
14+
<input type="text" wire:model.fill="fill" />
15+
<input type="text" wire:model.number="number" />
1416
<a href="/test" wire:click.prevent="prevent">test</a>
1517
<x-button wire:click="submit" />
1618
<x-button wire:click='singlequote' />

0 commit comments

Comments
 (0)