illegal offset type in tests

I have many of my tests fail with the same error, and not sure why. As an example test that is failing, its complaining about the ->test line
public function test_missing_fields_when_creating_user(): void
{
$this->tempUser->assignRole(RoleConstants::SUPER_ADMIN);

Livewire::actingAs($this->tempUser)
->test(UserResource\Pages\CreateUser::class)
->call('create')
->assertHasErrors(['data.name', 'data.email', 'data.password', 'data.password_confirmation']);

$this->tempUser->removeRole(RoleConstants::SUPER_ADMIN);
}
public function test_missing_fields_when_creating_user(): void
{
$this->tempUser->assignRole(RoleConstants::SUPER_ADMIN);

Livewire::actingAs($this->tempUser)
->test(UserResource\Pages\CreateUser::class)
->call('create')
->assertHasErrors(['data.name', 'data.email', 'data.password', 'data.password_confirmation']);

$this->tempUser->removeRole(RoleConstants::SUPER_ADMIN);
}
No description
Solution:
I Finally found it. So basically where im creating the temp users, I have to cast the uuid as a string when creating ```php $user->id = (string) Str::uuid();...
Jump to solution
19 Replies
Jamie Cee
Jamie CeeOP2mo ago
And the page its referencing
class CreateUser extends CreateRecord
{
protected static string $resource = UserResource::class;

/**
* Redirect after save
*
* @return string
*/
protected function getRedirectUrl(): string
{
return $this->previousUrl ?? $this->getResource()::getUrl('index');
}

/**
* @param array<string, mixed> $data
*/
protected function handleRecordCreation(array $data): Model
{
/* Define the password so we can send in the instructions email */
$password = $data['password'];

/* Hash the password thats stored in the db */
$data['password'] = Hash::make($password);

/* Create the user */
$user = parent::handleRecordCreation($data);

/* Send the login instructions */
if (
array_key_exists('email_welcome_instructions', $data)
&& $data['email_welcome_instructions'] === true
&& !$user->sendLoginInstructions(password: $password)
) {
\Filament\Notifications\Notification::make()
->title('There was an error sending out instructions')
->danger()
->send();
}

return $user;
}
}
class CreateUser extends CreateRecord
{
protected static string $resource = UserResource::class;

/**
* Redirect after save
*
* @return string
*/
protected function getRedirectUrl(): string
{
return $this->previousUrl ?? $this->getResource()::getUrl('index');
}

/**
* @param array<string, mixed> $data
*/
protected function handleRecordCreation(array $data): Model
{
/* Define the password so we can send in the instructions email */
$password = $data['password'];

/* Hash the password thats stored in the db */
$data['password'] = Hash::make($password);

/* Create the user */
$user = parent::handleRecordCreation($data);

/* Send the login instructions */
if (
array_key_exists('email_welcome_instructions', $data)
&& $data['email_welcome_instructions'] === true
&& !$user->sendLoginInstructions(password: $password)
) {
\Filament\Notifications\Notification::make()
->title('There was an error sending out instructions')
->danger()
->send();
}

return $user;
}
}
THese tests used to work, until I implemented the spatie perrmissions plugin
awcodes
awcodes2mo ago
Make sure you are seeding the roles and permissions too.
Jamie Cee
Jamie CeeOP2mo ago
I have this in my TestCase setup flow
protected function createUserRecord(): User
{
/* Allow us to use faker */
$faker = \Faker\Factory::create('en_GB');

/* Create a user - no idea why the factory method would not work */
$user = new User();
$user->id = Str::uuid();
$user->name = $faker->name();
$user->email = $faker->unique()->userName() . \Illuminate\Support\Str::random(12) . '@example.com';
$user->email_verified_at = null;
$user->password = Hash::make('Password1!'); //password
$user->save();

if (!Role::where('name', RoleConstants::SUPER_ADMIN)->first()) {
Role::create(['name' => RoleConstants::SUPER_ADMIN]);
}

/* Give user Super Admin */
$user->assignRole(RoleConstants::SUPER_ADMIN);

return $user;
}
protected function createUserRecord(): User
{
/* Allow us to use faker */
$faker = \Faker\Factory::create('en_GB');

/* Create a user - no idea why the factory method would not work */
$user = new User();
$user->id = Str::uuid();
$user->name = $faker->name();
$user->email = $faker->unique()->userName() . \Illuminate\Support\Str::random(12) . '@example.com';
$user->email_verified_at = null;
$user->password = Hash::make('Password1!'); //password
$user->save();

if (!Role::where('name', RoleConstants::SUPER_ADMIN)->first()) {
Role::create(['name' => RoleConstants::SUPER_ADMIN]);
}

/* Give user Super Admin */
$user->assignRole(RoleConstants::SUPER_ADMIN);

return $user;
}
Done a dd in the TestCase, I can see the role definitely exists, and returns true if I check the user having the role
awcodes
awcodes2mo ago
And what’s in the policy?
Jamie Cee
Jamie CeeOP2mo ago
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->checkPermissionTo('User.view-any') || $user->canManageAnyOrganisations();
}

/**
* Determine whether the user can view the model.
*/
public function view(User $user, User $model): bool
{
return $user->checkPermissionTo('User.view');
}

/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->checkPermissionTo('User.create');
}
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->checkPermissionTo('User.view-any') || $user->canManageAnyOrganisations();
}

/**
* Determine whether the user can view the model.
*/
public function view(User $user, User $model): bool
{
return $user->checkPermissionTo('User.view');
}

/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->checkPermissionTo('User.create');
}
Along the lines of this, but I've even tested by just returning true in every function, same outcome
awcodes
awcodes2mo ago
Hm. Filament honors the policy and your policy is checking permissions not roles. But I would expect it to work with true.
Jamie Cee
Jamie CeeOP2mo ago
I've just tried adding ->givePermissionTo() on the user, but still getting the same Illegal offset type error. Im just so confused But even then, its Super Admin im adding, does that not cover all permissions?
awcodes
awcodes2mo ago
Permissions and Roles are separate in Spatie's package when you seed the roles you also have to seed the permissions and assign the permissions to the roles if you want to use both
Jamie Cee
Jamie CeeOP2mo ago
Im not actually wiping the database currently during these tests, so those permissions do already exist in the db.
awcodes
awcodes2mo ago
do you have a repo you can share?
Jamie Cee
Jamie CeeOP2mo ago
Sadly its a work one, so NDA. I can try mimic as much of this as possible to remove redacted information to share. What particularly would you be looking for?
awcodes
awcodes2mo ago
i'm using pest, but i have these helpers:
function loginAsUser(string $role, string $permission, ?User $user = null): User
{
$roleModel = Role::create(['name' => "Manage $role"]);
$view = Permission::create(['name' => "view $permission"]);
$create = Permission::create(['name' => "create $permission"]);
$update = Permission::create(['name' => "update $permission"]);
$delete = Permission::create(['name' => "delete $permission"]);

$roleModel->givePermissionTo([$view, $update, $create, $delete]);

$user = $user ?? User::factory()->create();
$user->assignRole("Manage $role");
actingAs($user);

return $user;
}

function createRole(string $role, string $permission): Role
{
$roleModel = Role::create(['name' => "Manage $role"]);
$view = Permission::create(['name' => "view $permission"]);
$create = Permission::create(['name' => "create $permission"]);
$update = Permission::create(['name' => "update $permission"]);
$delete = Permission::create(['name' => "delete $permission"]);

$roleModel->givePermissionTo([$view, $update, $create, $delete]);

return $roleModel;
}
function loginAsUser(string $role, string $permission, ?User $user = null): User
{
$roleModel = Role::create(['name' => "Manage $role"]);
$view = Permission::create(['name' => "view $permission"]);
$create = Permission::create(['name' => "create $permission"]);
$update = Permission::create(['name' => "update $permission"]);
$delete = Permission::create(['name' => "delete $permission"]);

$roleModel->givePermissionTo([$view, $update, $create, $delete]);

$user = $user ?? User::factory()->create();
$user->assignRole("Manage $role");
actingAs($user);

return $user;
}

function createRole(string $role, string $permission): Role
{
$roleModel = Role::create(['name' => "Manage $role"]);
$view = Permission::create(['name' => "view $permission"]);
$create = Permission::create(['name' => "create $permission"]);
$update = Permission::create(['name' => "update $permission"]);
$delete = Permission::create(['name' => "delete $permission"]);

$roleModel->givePermissionTo([$view, $update, $create, $delete]);

return $roleModel;
}
then in my tests i use loginAsUser(role: 'Pages', permission: 'page'); to login the user with the appropriate permissions and role. and it works.
Jamie Cee
Jamie CeeOP2mo ago
Ill try replicate and give it a go
awcodes
awcodes2mo ago
try using ->can() instead of ->checkPermissionTo(). I think that might be an internal method. The docs say to use ->can
Jamie Cee
Jamie CeeOP2mo ago
The checkPermissionTo() came with the generation command. Apologies, im also using the https://github.com/althinect/filament-spatie-roles-permissions repo
GitHub
GitHub - Althinect/filament-spatie-roles-permissions
Contribute to Althinect/filament-spatie-roles-permissions development by creating an account on GitHub.
awcodes
awcodes2mo ago
ah, don't know anything about the package. sorry. might get better support if you ask the question directly on that channel. #althinect-spatie-roles-permissions
Jamie Cee
Jamie CeeOP2mo ago
WIll do. Thank you
awcodes
awcodes2mo ago
if it's something in the package then it's not a filament question. 🙂
Solution
Jamie Cee
Jamie Cee2mo ago
I Finally found it. So basically where im creating the temp users, I have to cast the uuid as a string when creating
$user->id = (string) Str::uuid();
$user->id = (string) Str::uuid();
Else it was detecting as a LazyLoadFromUuid or some named object
Want results from more Discord servers?
Add your server