181 lines
7.4 KiB
PHP
181 lines
7.4 KiB
PHP
<?php
|
|
|
|
use App\Livewire\EditSeat;
|
|
use App\Livewire\Users;
|
|
use App\Models\Customer;
|
|
use App\Models\Instance;
|
|
use App\Models\Order;
|
|
use App\Models\Seat;
|
|
use App\Models\User;
|
|
use Livewire\Livewire;
|
|
|
|
function seatSetup(string $plan = 'team'): array
|
|
{
|
|
$user = User::factory()->create(['email' => 'o@seat.test', 'is_admin' => false]);
|
|
$customer = Customer::factory()->create(['email' => 'o@seat.test', 'user_id' => $user->id, 'name' => 'Owner Co']);
|
|
$order = Order::factory()->create(['customer_id' => $customer->id, 'plan' => $plan]);
|
|
Instance::factory()->create(['customer_id' => $customer->id, 'order_id' => $order->id, 'plan' => $plan, 'status' => 'active']);
|
|
|
|
return compact('user', 'customer');
|
|
}
|
|
|
|
it('creates an owner seat on first visit', function () {
|
|
['user' => $user, 'customer' => $customer] = seatSetup();
|
|
|
|
Livewire::actingAs($user)->test(Users::class)->assertOk();
|
|
|
|
$owner = $customer->seats()->where('role', 'owner')->first();
|
|
expect($owner)->not->toBeNull()->and($owner->email)->toBe('o@seat.test');
|
|
});
|
|
|
|
it('invites a seat and blocks duplicates', function () {
|
|
['user' => $user, 'customer' => $customer] = seatSetup();
|
|
|
|
Livewire::actingAs($user)->test(Users::class)
|
|
->set('inviteEmail', 'new@seat.test')->set('inviteRole', 'member')->call('invite')
|
|
->assertHasNoErrors();
|
|
expect($customer->seats()->where('email', 'new@seat.test')->exists())->toBeTrue();
|
|
|
|
Livewire::actingAs($user)->test(Users::class)
|
|
->set('inviteEmail', 'new@seat.test')->call('invite')
|
|
->assertHasErrors(['inviteEmail']);
|
|
expect($customer->seats()->where('email', 'new@seat.test')->count())->toBe(1);
|
|
});
|
|
|
|
it('enforces the plan seat limit', function () {
|
|
['user' => $user, 'customer' => $customer] = seatSetup('start'); // 5-seat plan
|
|
// Fill all 5 seats so the plan allowance is exhausted.
|
|
Seat::factory()->count(5)->create(['customer_id' => $customer->id]);
|
|
|
|
Livewire::actingAs($user)->test(Users::class)
|
|
->set('inviteEmail', 'over@seat.test')->call('invite')
|
|
->assertHasErrors(['inviteEmail']);
|
|
expect($customer->seats()->where('email', 'over@seat.test')->exists())->toBeFalse();
|
|
});
|
|
|
|
it('will not remove or demote the last owner', function () {
|
|
['user' => $user, 'customer' => $customer] = seatSetup();
|
|
Livewire::actingAs($user)->test(Users::class); // creates owner
|
|
$owner = $customer->seats()->where('role', 'owner')->first();
|
|
|
|
Livewire::actingAs($user)->test(Users::class)->call('revoke', $owner->uuid);
|
|
expect($customer->seats()->whereKey($owner->id)->exists())->toBeTrue();
|
|
|
|
Livewire::actingAs($user)->test(Users::class)->call('setRole', $owner->uuid, 'member');
|
|
expect($owner->fresh()->role)->toBe('owner');
|
|
});
|
|
|
|
it('revokes a non-owner seat', function () {
|
|
['user' => $user, 'customer' => $customer] = seatSetup();
|
|
$seat = Seat::factory()->create(['customer_id' => $customer->id, 'role' => 'member']);
|
|
|
|
Livewire::actingAs($user)->test(Users::class)->call('revoke', $seat->uuid);
|
|
expect($customer->seats()->whereKey($seat->id)->exists())->toBeFalse();
|
|
});
|
|
|
|
it('shows the actions column even when the owner is the only seat', function () {
|
|
// Reported three times: a customer with one seat saw a table with nothing
|
|
// to click and concluded the product could not edit users. The column used
|
|
// to be hidden when there was "nothing to act on" — but a column that
|
|
// disappears does not read as "not applicable here", it reads as missing.
|
|
$user = User::factory()->create();
|
|
$customer = Customer::factory()->create(['user_id' => $user->id, 'email' => $user->email]);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(Users::class)
|
|
->assertSee(__('users.col_actions'))
|
|
->assertSee(__('users.edit'))
|
|
// And it says why the owner has no destructive actions instead of
|
|
// leaving an empty cell.
|
|
->assertSee(__('users.owner_protected'));
|
|
|
|
expect($customer->seats()->count())->toBe(1);
|
|
});
|
|
|
|
it('renames any seat, the owner included', function () {
|
|
$user = User::factory()->create();
|
|
$customer = Customer::factory()->create(['user_id' => $user->id, 'email' => $user->email]);
|
|
$owner = $customer->seats()->create([
|
|
'email' => $user->email, 'name' => 'Falsch Geschrieben', 'role' => 'owner', 'status' => 'active',
|
|
]);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(EditSeat::class, ['uuid' => $owner->uuid])
|
|
->assertSet('name', 'Falsch Geschrieben')
|
|
->assertSet('isOwner', true)
|
|
->set('name', 'Dr. M. Muster')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
expect($owner->fresh()->name)->toBe('Dr. M. Muster');
|
|
});
|
|
|
|
it('corrects the address of an invitation still in flight', function () {
|
|
$user = User::factory()->create();
|
|
$customer = Customer::factory()->create(['user_id' => $user->id, 'email' => $user->email]);
|
|
$seat = $customer->seats()->create([
|
|
'email' => 'tpyo@example.test', 'role' => 'member', 'status' => 'invited',
|
|
]);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(EditSeat::class, ['uuid' => $seat->uuid])
|
|
->assertSet('addressEditable', true)
|
|
->set('email', 'typo@example.test')
|
|
->call('save')
|
|
->assertHasNoErrors();
|
|
|
|
expect($seat->fresh()->email)->toBe('typo@example.test');
|
|
});
|
|
|
|
it('will not move an accepted seat to a different address', function () {
|
|
// The address IS the person once they are in. Editing it would hand one
|
|
// employee's access to another with nobody told — a transfer of access
|
|
// wearing the clothes of a rename. Driven past the form on purpose: the
|
|
// property is hydrated from the browser and must not be trusted.
|
|
$user = User::factory()->create();
|
|
$customer = Customer::factory()->create(['user_id' => $user->id, 'email' => $user->email]);
|
|
$seat = $customer->seats()->create([
|
|
'email' => 'in@example.test', 'name' => 'A', 'role' => 'member', 'status' => 'active',
|
|
]);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(EditSeat::class, ['uuid' => $seat->uuid])
|
|
->assertSet('addressEditable', false)
|
|
->set('addressEditable', true)
|
|
->set('email', 'someone.else@example.test')
|
|
->set('name', 'B')
|
|
->call('save');
|
|
|
|
expect($seat->fresh()->email)->toBe('in@example.test')
|
|
->and($seat->fresh()->name)->toBe('B');
|
|
});
|
|
|
|
it('refuses to open a seat belonging to somebody else', function () {
|
|
// The uuid comes from the browser, and a modal is reachable without the
|
|
// page's route middleware.
|
|
$mine = User::factory()->create();
|
|
Customer::factory()->create(['user_id' => $mine->id, 'email' => $mine->email]);
|
|
|
|
$theirs = Customer::factory()->create(['user_id' => User::factory()->create()->id]);
|
|
$seat = $theirs->seats()->create(['email' => 'x@example.test', 'name' => 'X', 'role' => 'member', 'status' => 'active']);
|
|
|
|
Livewire::actingAs($mine)
|
|
->test(EditSeat::class, ['uuid' => $seat->uuid])
|
|
->assertStatus(404);
|
|
|
|
expect($seat->fresh()->name)->toBe('X');
|
|
});
|
|
|
|
it('opens editing in a modal rather than in the row', function () {
|
|
// R20. Inline fields made the row grow and the columns beside it jump,
|
|
// which reads as a rendering fault rather than as a form.
|
|
$user = User::factory()->create();
|
|
Customer::factory()->create(['user_id' => $user->id, 'email' => $user->email]);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(Users::class)
|
|
->assertSee('openModal', escape: false)
|
|
->assertSee('edit-seat', escape: false)
|
|
->assertDontSee('wire:model="editName"', escape: false);
|
|
});
|