mirror of
https://github.com/ericasch6/scrum-poker-internal.git
synced 2026-10-13 15:32:09 +00:00
Add room participant moderation
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\RoomStateChange;
|
||||
use App\Events\RoomStateChanged;
|
||||
use App\Models\Player;
|
||||
use App\Models\Room;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Inertia\Testing\AssertableInertia as Assert;
|
||||
|
||||
function createRoomForPlayerKick(User $owner, array $attributes = []): Room
|
||||
{
|
||||
return Room::create([
|
||||
'owner_id' => $owner->id,
|
||||
'title' => 'Team Planning',
|
||||
'current_round' => 1,
|
||||
'deck_config' => [
|
||||
'name' => 'Fibonacci',
|
||||
'cards' => [
|
||||
['key' => '3', 'label' => '3'],
|
||||
['key' => '5', 'label' => '5'],
|
||||
],
|
||||
],
|
||||
...$attributes,
|
||||
]);
|
||||
}
|
||||
|
||||
function addPlayerToKickRoom(
|
||||
Room $room,
|
||||
string $name,
|
||||
?User $user = null,
|
||||
bool $isModerator = false,
|
||||
): Player {
|
||||
return $room->players()->create([
|
||||
'user_id' => $user?->id,
|
||||
'name' => $name,
|
||||
'is_moderator' => $isModerator,
|
||||
'last_seen_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
test('the room owner sees active room players on the dashboard', function () {
|
||||
$owner = User::factory()->create();
|
||||
$room = createRoomForPlayerKick($owner);
|
||||
$moderator = addPlayerToKickRoom($room, 'Moderator', $owner, true);
|
||||
$participant = addPlayerToKickRoom($room, 'Anna');
|
||||
addPlayerToKickRoom($room, 'Inactive')->update(['inactive_at' => now()]);
|
||||
|
||||
$this->actingAs($owner)
|
||||
->get(route('dashboard'))
|
||||
->assertInertia(fn (Assert $page) => $page
|
||||
->has('rooms.0.players', 2)
|
||||
->where('rooms.0.players.0.id', $moderator->id)
|
||||
->where('rooms.0.players.0.is_moderator', true)
|
||||
->where('rooms.0.players.0.is_owner', true)
|
||||
->where('rooms.0.players.1.id', $participant->id)
|
||||
->where('rooms.0.players.1.is_moderator', false)
|
||||
->where('rooms.0.players.1.is_owner', false));
|
||||
});
|
||||
|
||||
test('the room owner can kick an active participant', function () {
|
||||
$owner = User::factory()->create();
|
||||
$room = createRoomForPlayerKick($owner);
|
||||
addPlayerToKickRoom($room, 'Moderator', $owner, true);
|
||||
$participant = addPlayerToKickRoom($room, 'Anna');
|
||||
$remainingPlayer = addPlayerToKickRoom($room, 'Ben');
|
||||
$remainingPlayer->votes()->create([
|
||||
'room_id' => $room->id,
|
||||
'round' => $room->current_round,
|
||||
'value' => '5',
|
||||
]);
|
||||
$previousVersion = $room->fresh()->state_version;
|
||||
|
||||
Event::fake([RoomStateChanged::class]);
|
||||
|
||||
$this->actingAs($owner)
|
||||
->delete(route('room.player.destroy', [$room, $participant]))
|
||||
->assertRedirect();
|
||||
|
||||
expect($participant->fresh()->inactive_at)->not->toBeNull()
|
||||
->and($room->fresh()->state_version)->toBe($previousVersion + 1);
|
||||
|
||||
Event::assertDispatched(
|
||||
RoomStateChanged::class,
|
||||
function (RoomStateChanged $event) use ($participant, $remainingPlayer, $room): bool {
|
||||
if (
|
||||
$event->roomId !== $room->id
|
||||
|| $event->change !== RoomStateChange::PlayerKicked
|
||||
|| $event->affectedPlayerId !== $participant->id
|
||||
|| $event->state === null
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$remainingPlayerState = collect($event->state['players'])
|
||||
->firstWhere('id', $remainingPlayer->id);
|
||||
|
||||
return collect($event->state['players'])->doesntContain('id', $participant->id)
|
||||
&& $remainingPlayerState['has_voted'] === true
|
||||
&& $remainingPlayerState['vote'] === null
|
||||
&& $event->broadcastWith()['affected_player_id'] === $participant->id;
|
||||
},
|
||||
);
|
||||
Event::assertDispatchedTimes(RoomStateChanged::class, 1);
|
||||
});
|
||||
|
||||
test('a participant cannot kick another player', function () {
|
||||
$owner = User::factory()->create();
|
||||
$participantUser = User::factory()->create();
|
||||
$room = createRoomForPlayerKick($owner);
|
||||
addPlayerToKickRoom($room, 'Moderator', $owner, true);
|
||||
addPlayerToKickRoom($room, 'Anna', $participantUser);
|
||||
$target = addPlayerToKickRoom($room, 'Ben');
|
||||
|
||||
Event::fake([RoomStateChanged::class]);
|
||||
|
||||
$this->actingAs($participantUser)
|
||||
->delete(route('room.player.destroy', [$room, $target]))
|
||||
->assertForbidden();
|
||||
|
||||
expect($target->fresh()->inactive_at)->toBeNull();
|
||||
Event::assertNotDispatched(RoomStateChanged::class);
|
||||
});
|
||||
|
||||
test('the room owner cannot kick their own player', function () {
|
||||
$owner = User::factory()->create();
|
||||
$room = createRoomForPlayerKick($owner);
|
||||
$moderator = addPlayerToKickRoom($room, 'Moderator', $owner, true);
|
||||
|
||||
Event::fake([RoomStateChanged::class]);
|
||||
|
||||
$this->actingAs($owner)
|
||||
->delete(route('room.player.destroy', [$room, $moderator]))
|
||||
->assertForbidden();
|
||||
|
||||
expect($moderator->fresh()->inactive_at)->toBeNull();
|
||||
Event::assertNotDispatched(RoomStateChanged::class);
|
||||
});
|
||||
|
||||
test('the room owner cannot kick a player from another room', function () {
|
||||
$owner = User::factory()->create();
|
||||
$otherOwner = User::factory()->create();
|
||||
$room = createRoomForPlayerKick($owner);
|
||||
$otherRoom = createRoomForPlayerKick($otherOwner, ['title' => 'Other room']);
|
||||
$target = addPlayerToKickRoom($otherRoom, 'Anna');
|
||||
|
||||
Event::fake([RoomStateChanged::class]);
|
||||
|
||||
$this->actingAs($owner)
|
||||
->delete(route('room.player.destroy', [$room, $target]))
|
||||
->assertForbidden();
|
||||
|
||||
expect($target->fresh()->inactive_at)->toBeNull();
|
||||
Event::assertNotDispatched(RoomStateChanged::class);
|
||||
});
|
||||
Reference in New Issue
Block a user