Testing Table Action with modal

My action code is
public function removeAction(): Action
{
ray('running remove action');

return Action::make('removeAction')
->label('Remove')
->icon('heroicon-o-x-circle')
->color('danger')
->modalHeading(fn($record) => 'Remove ' . $record->user->name)
->modalDescription(fn($record) => 'Are you sure you want to de-associate this property manager?')
->action(function ($record) {
ray($record);
return $record->update(['property_management_company_id' => null]);
})
->successNotificationTitle('Property manager has been de-associated.')
->visible($this->propertyManagementCompany !== null)
->after(fn() => $this->emitTo(ViewPropertyManagementCompany::class, '$refresh'));
}
public function removeAction(): Action
{
ray('running remove action');

return Action::make('removeAction')
->label('Remove')
->icon('heroicon-o-x-circle')
->color('danger')
->modalHeading(fn($record) => 'Remove ' . $record->user->name)
->modalDescription(fn($record) => 'Are you sure you want to de-associate this property manager?')
->action(function ($record) {
ray($record);
return $record->update(['property_management_company_id' => null]);
})
->successNotificationTitle('Property manager has been de-associated.')
->visible($this->propertyManagementCompany !== null)
->after(fn() => $this->emitTo(ViewPropertyManagementCompany::class, '$refresh'));
}
with my test being
it('disassociate property manager with property management company', function () {
/* @phpstan-ignore-next-line */
$user = login();
/* @phpstan-ignore-next-line */
$this->withSession(['tenant' => $user->tenant]);

$propertyManagementCompany = PropertyManagementCompany::factory()->create();
$propertyManager = PropertyManager::factory()->create([
'property_management_company_id' => $propertyManagementCompany->id,
]);

/* @phpstan-ignore-next-line */
$this->get('/property-managers/companies/' . $propertyManagementCompany->id);

Livewire::test(PropertyManagersTable::class, ['propertyManagementCompany' => $propertyManagementCompany])
->assertTableActionExists('removeAction')
->callTableAction('removeAction', record: $propertyManager)
->assertHasNoErrors();

expect($propertyManager->fresh()->property_management_company_id)->toBeNull();
});
it('disassociate property manager with property management company', function () {
/* @phpstan-ignore-next-line */
$user = login();
/* @phpstan-ignore-next-line */
$this->withSession(['tenant' => $user->tenant]);

$propertyManagementCompany = PropertyManagementCompany::factory()->create();
$propertyManager = PropertyManager::factory()->create([
'property_management_company_id' => $propertyManagementCompany->id,
]);

/* @phpstan-ignore-next-line */
$this->get('/property-managers/companies/' . $propertyManagementCompany->id);

Livewire::test(PropertyManagersTable::class, ['propertyManagementCompany' => $propertyManagementCompany])
->assertTableActionExists('removeAction')
->callTableAction('removeAction', record: $propertyManager)
->assertHasNoErrors();

expect($propertyManager->fresh()->property_management_company_id)->toBeNull();
});
, while it runs the action method, it never seems to get to the removeAction method, it never gets to the ->action() within it. Only my tests are failing. Its working great in the actual browser. Im assuming i need to call something else?
28 Replies
Mark Chaney
Mark ChaneyOP16mo ago
Anyone have any ideas? The really odd thing here is that it never errors until it does the expect(), so the assert that it exists passes, the call passes, the no error passes, it just never seems to run the action(). Ive even taken off all the modal/confirmation off the action so that it runs direct and i still get the same result of the action() never executing
Patrick Boivin
Patrick Boivin16mo ago
Hmm, I'm not sure if I can really help but I'm curious to dig in a bit... so it fails to assert that property_management_company_id is null?
Mark Chaney
Mark ChaneyOP16mo ago
@pboivin correct. Ive verified with just a dump of $propertyManager->fresh()->property_management_company_id that it is indeed still there as well. Just really odd that in real world everything works perfectly. ive tried a bunch of refactors and cant get the test to work. Its not like this is the first table row ive tested before. Though the others were either a delete or an edit
Patrick Boivin
Patrick Boivin16mo ago
And just for context, how is removeAction() setup in the rest of the table? What calls that method?
Mark Chaney
Mark ChaneyOP16mo ago
@pboivin ->actions([$this->removeAction()])
Patrick Boivin
Patrick Boivin16mo ago
Ok I see, so when you run the test, what output are you seeing in Ray? (I'm assuming nothing...?)
Mark Chaney
Mark ChaneyOP16mo ago
only running remove action twice. but that obviously runs simply when the table is rendered. Ive even added a ray() to modalHeading() and that doesnt run
Patrick Boivin
Patrick Boivin16mo ago
Oh right, yeah So the Action class is instantiated but none of the callbacks are executed at runtime through the test
Mark Chaney
Mark ChaneyOP16mo ago
yep. i must be missing something simple
Patrick Boivin
Patrick Boivin16mo ago
Can you try ->callTableAction('asdf', record: $propertyManager), just to make sure that fails?
Mark Chaney
Mark ChaneyOP16mo ago
@pboivin it does fail I refactored a bit since yesterday
test('disassociate property manager with property management company', function () {

// create a user with a property manager role
$user = User::factory()->propertyManager()->create();

/* @phpstan-ignore-next-line */
login($user);
/* @phpstan-ignore-next-line */
$this->withSession(['tenant' => $user->tenant]);

// create a property management company and assign the PM to it
$propertyManagementCompany = PropertyManagementCompany::factory()->create();
$user->property_manager->update([
'property_management_company_id' => $propertyManagementCompany->id,
]);

/* @phpstan-ignore-next-line */
$this->assertModelExists($user->property_manager);

/* @phpstan-ignore-next-line */
$this->get('/property-managers/companies/'.$propertyManagementCompany->id);

Livewire::test(PropertyManagersTable::class, ['propertyManagementCompany' => $propertyManagementCompany])
->assertTableActionExists('remove_action')
->assertTableActionVisible('remove_action')
->callTableAction('remove_action', record: $user->property_manager)
->assertHasNoTableActionErrors();

expect($user->property_manager->fresh()->property_management_company_id)->toBeNull();
});
test('disassociate property manager with property management company', function () {

// create a user with a property manager role
$user = User::factory()->propertyManager()->create();

/* @phpstan-ignore-next-line */
login($user);
/* @phpstan-ignore-next-line */
$this->withSession(['tenant' => $user->tenant]);

// create a property management company and assign the PM to it
$propertyManagementCompany = PropertyManagementCompany::factory()->create();
$user->property_manager->update([
'property_management_company_id' => $propertyManagementCompany->id,
]);

/* @phpstan-ignore-next-line */
$this->assertModelExists($user->property_manager);

/* @phpstan-ignore-next-line */
$this->get('/property-managers/companies/'.$propertyManagementCompany->id);

Livewire::test(PropertyManagersTable::class, ['propertyManagementCompany' => $propertyManagementCompany])
->assertTableActionExists('remove_action')
->assertTableActionVisible('remove_action')
->callTableAction('remove_action', record: $user->property_manager)
->assertHasNoTableActionErrors();

expect($user->property_manager->fresh()->property_management_company_id)->toBeNull();
});
If I do switchout the call action that should fail, i do get:
Failed asserting that a table action with name [asdf] exists on the [App\Http\Livewire\PropertyManagers\PropertyManagersTable] component.
Failed asserting that null is an instance of class "Filament\Tables\Actions\Action".
Failed asserting that a table action with name [asdf] exists on the [App\Http\Livewire\PropertyManagers\PropertyManagersTable] component.
Failed asserting that null is an instance of class "Filament\Tables\Actions\Action".
Patrick Boivin
Patrick Boivin16mo ago
Ok nice, I know it's a bit dumb but you never know 😄
Mark Chaney
Mark ChaneyOP16mo ago
oh absolutely and i appreciate the help and ideas
Patrick Boivin
Patrick Boivin16mo ago
Have you been digging a bit into vendor/filament/tables/src/Testing/TestsActions.php? I'm wondering if (for some unknown reason) you could be getting an early return of $this before your action gets called.
Mark Chaney
Mark ChaneyOP16mo ago
I have, but not super in depth
Patrick Boivin
Patrick Boivin16mo ago
public function callTableAction(): Closure
{
return function (string | array $name, $record = null, array $data = [], array $arguments = []): static {
/** @phpstan-ignore-next-line */
$this->assertTableActionVisible($name, $record);

/** @phpstan-ignore-next-line */
$this->mountTableAction($name, $record);

if (! $this->instance()->getMountedTableAction()) {
return $this;
}

/// Are you getting to this point? ///

/** @phpstan-ignore-next-line */
$this->setTableActionData($data);

/** @phpstan-ignore-next-line */
$this->callMountedTableAction($arguments);

return $this;
};
}
public function callTableAction(): Closure
{
return function (string | array $name, $record = null, array $data = [], array $arguments = []): static {
/** @phpstan-ignore-next-line */
$this->assertTableActionVisible($name, $record);

/** @phpstan-ignore-next-line */
$this->mountTableAction($name, $record);

if (! $this->instance()->getMountedTableAction()) {
return $this;
}

/// Are you getting to this point? ///

/** @phpstan-ignore-next-line */
$this->setTableActionData($data);

/** @phpstan-ignore-next-line */
$this->callMountedTableAction($arguments);

return $this;
};
}
Mark Chaney
Mark ChaneyOP16mo ago
you nailed it, its getting stopped by
if (! $this->instance()->getMountedTableAction()) {
return $this;
}
if (! $this->instance()->getMountedTableAction()) {
return $this;
}
as i temporarily edited it to have
ray($record)->label('before getMountedTableAction() check');

if (! $this->instance()->getMountedTableAction()) {
return $this;
}

ray($record)->label('after getMountedTableAction() check');
ray($record)->label('before getMountedTableAction() check');

if (! $this->instance()->getMountedTableAction()) {
return $this;
}

ray($record)->label('after getMountedTableAction() check');
and the before runs, but not the after
Patrick Boivin
Patrick Boivin16mo ago
Hmm, getting closer! So now is the issue in mountTableAction() or getMountedTableAction()? 🤔
Mark Chaney
Mark ChaneyOP16mo ago
yep, looks like it
Patrick Boivin
Patrick Boivin16mo ago
Another sanity check... is your action extending Filament\Tables\Actions\Action? oops
Mark Chaney
Mark ChaneyOP16mo ago
yes, 100%
Patrick Boivin
Patrick Boivin16mo ago
Is PropertyManagersTable a custom Livewire component? At this point this is the only difference I'm seeing. I just tried with a regular List page and my test is correctly calling the action.
Mark Chaney
Mark ChaneyOP16mo ago
@pboivin yes. my entire app is standalone So you did a custom action that was not related to edit or delete or any standard action?
Patrick Boivin
Patrick Boivin16mo ago
Yeah, just tested on a List page and a custom Livewire component. Both worked. Are you on the latest v3.0.4?
Mark Chaney
Mark ChaneyOP16mo ago
I am not as i cant upgrade to livewire 3 yet. im on the latest alpha. I didnt see anything different in regards to these affected classes though. Maybe i should spin up the the latest demo and give it a try
Patrick Boivin
Patrick Boivin16mo ago
Yeah, in the off chance it could have been an issue in the alpha... but I'm a bit out of my depth, I've only been exploring v3 since the beta release.
Mark Chaney
Mark ChaneyOP16mo ago
we have been using it in production since March 😛
pinoooh
pinoooh10mo ago
Just ran into the same problem. Reading through this post helped me fix it: mount the action before you call it:
Livewire::test(PropertyManagersTable::class, ['propertyManagementCompany' => $propertyManagementCompany])
->mountTableAction('remove_action')
->callTableAction('remove_action', record: $user->property_manager)
->assertHasNoTableActionErrors();
Livewire::test(PropertyManagersTable::class, ['propertyManagementCompany' => $propertyManagementCompany])
->mountTableAction('remove_action')
->callTableAction('remove_action', record: $user->property_manager)
->assertHasNoTableActionErrors();
this worked for me, so thanks!
Want results from more Discord servers?
Add your server