I have a component class that looks like this:
<?php
namespace App\Http\Livewire\Dashboard;
use App\Models\Team;
use App\Models\User;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Livewire\Component;
class LocationTracker extends Component
{
public Team $team;
public bool $tracking = false;
public function mount(): void{
$this->tracking = $this->getTrackingProperty();
}
public function track(): void{
if(!auth()->user()->can('lockLocation', $this->team)){
$this->tracking = false;
return;
}
$user = null;
if(!$this->team->trackedUser){
$user = auth()->user();
}
$this->team->trackedUser()->associate($user);
$this->team->save();
$this->tracking = !!$this->team->trackedUser;
}
/**
* @return bool The value of the $tracking property is a boolean
* indicating whether or not this team's location is being tracked
*/
public function getTrackingProperty(): bool{
return $this->team->trackedUser instanceof User;
}
public function render(): Factory|View|Application
{
return view('livewire.dashboard.location-tracker');
}
}
The corresponding Livewire template looks like this:
<div wire:key="team-id-{{ $team->id }}" class="location-tracker-toggle-container">
<label for="location-tracker-toggle"></label>
<input
id="location-tracker-toggle"
type="checkbox"
wire:model="tracking"
wire:change.prevent="track"
wire:loading.attr="disabled"
/>
</div>
This template is in a Blade component that looks like this:
<div class="team-container">
<div {{ $isCurrentTeam() ? 'id=current-team' : '' }}
{{ $attributes->merge(['class' => $getCssClasses ]) }}
data-team-id="{{ $team?->id }}"
>
<h1 class="team-name">{{ $team?->name }}</h1>
<livewire:dashboard.location-tracker :team="$team"/>
</div>
</div>
Basically, LocationTracker is expecting a Team property so that the user can track their location on behalf of the Team. There is a one-to-one relationship setup where the teams table has a tracked_user_id column that refers to users.id.
All I want is to write a simple test:
Livewire::actingAs($admin)
->test(LocationTracker::class)
->set('team', $admin->currentTeam)
->call('track')
->assertSet('tracking', true);
so I can confirm that a team admin can track their location. The $team property needs to be set, but when I do it like this, I get an error: Typed property App\Http\Livewire\Dashboard\LocationTracker::$team must not be accessed before initialization, probably because of the mount method. When I set the $team property to a nullable Team and change every method call on a team to use the nullable property accessor so that it is okay with a null value, I then get an odd error about PHP, or rather Xdebug, detecting an infinite loop after "a stack depth of '256' frames".
I also get the infinite loop error if I try to set team with an array as the second parameter to test. Why can't this test simply set team to the model object property of the LocationTracker component?:
Xdebug has detected a possible infinite loop, and aborted your script with a stack depth of '256' frames (View: /var/www/html/vendor/livewire/livewire/src/views/mount-component.blade.php)
at vendor/laravel/framework/src/Illuminate/Collections/HigherOrderCollectionProxy.php:59
55▕ * @return mixed
56▕ */
57▕ public function __call($method, $parameters)
58▕ {
➜ 59▕ return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
60▕ return $value->{$method}(...$parameters);
61▕ });
62▕ }
63▕ }
+3 vendor frames
4 [internal]:0
Illuminate\Support\HigherOrderCollectionProxy::Illuminate\Support\{closure}(Object(App\Models\Team))
+6 vendor frames
11 [internal]:0
Illuminate\Support\HigherOrderCollectionProxy::Illuminate\Support\{closure}(Object(App\Models\User))
In the actual web app, this works perfectly fine. Am I missing something or does this component test feature really not work as advertised?