Files
jackboxpartypack-gamepicker/tests/api/ecast-shard-client.test.js
2026-03-20 11:09:05 -04:00

174 lines
6.3 KiB
JavaScript

const { EcastShardClient } = require('../../backend/utils/ecast-shard-client');
describe('EcastShardClient', () => {
describe('parsePlayersFromHere', () => {
test('counts only player roles, excludes host and shard', () => {
const here = {
'1': { id: 1, roles: { host: {} } },
'2': { id: 2, roles: { player: { name: 'Alice' } } },
'3': { id: 3, roles: { player: { name: 'Bob' } } },
'5': { id: 5, roles: { shard: {} } },
};
const result = EcastShardClient.parsePlayersFromHere(here);
expect(result.playerCount).toBe(2);
expect(result.playerNames).toEqual(['Alice', 'Bob']);
});
test('returns zero for empty here or host-only', () => {
const here = { '1': { id: 1, roles: { host: {} } } };
const result = EcastShardClient.parsePlayersFromHere(here);
expect(result.playerCount).toBe(0);
expect(result.playerNames).toEqual([]);
});
test('handles null or undefined here', () => {
expect(EcastShardClient.parsePlayersFromHere(null).playerCount).toBe(0);
expect(EcastShardClient.parsePlayersFromHere(undefined).playerCount).toBe(0);
});
});
describe('parseRoomEntity', () => {
test('extracts lobby state from room entity val', () => {
const roomVal = {
state: 'Lobby',
lobbyState: 'CanStart',
gameCanStart: true,
gameIsStarting: false,
gameFinished: false,
};
const result = EcastShardClient.parseRoomEntity(roomVal);
expect(result.gameState).toBe('Lobby');
expect(result.lobbyState).toBe('CanStart');
expect(result.gameCanStart).toBe(true);
expect(result.gameStarted).toBe(false);
expect(result.gameFinished).toBe(false);
});
test('detects game started from Gameplay state', () => {
const roomVal = { state: 'Gameplay', lobbyState: 'Countdown', gameCanStart: true, gameIsStarting: false, gameFinished: false };
const result = EcastShardClient.parseRoomEntity(roomVal);
expect(result.gameStarted).toBe(true);
});
test('detects game finished', () => {
const roomVal = { state: 'Gameplay', lobbyState: '', gameCanStart: true, gameIsStarting: false, gameFinished: true };
const result = EcastShardClient.parseRoomEntity(roomVal);
expect(result.gameFinished).toBe(true);
});
});
describe('parsePlayerJoinFromTextDescriptions', () => {
test('extracts player name from join description', () => {
const val = {
latestDescriptions: [
{ category: 'TEXT_DESCRIPTION_PLAYER_JOINED', text: 'Charlie joined.' }
]
};
const result = EcastShardClient.parsePlayerJoinFromTextDescriptions(val);
expect(result).toEqual([{ name: 'Charlie', isVIP: false }]);
});
test('extracts VIP join', () => {
const val = {
latestDescriptions: [
{ category: 'TEXT_DESCRIPTION_PLAYER_JOINED_VIP', text: 'Alice joined and is the VIP.' }
]
};
const result = EcastShardClient.parsePlayerJoinFromTextDescriptions(val);
expect(result).toEqual([{ name: 'Alice', isVIP: true }]);
});
test('returns empty array for no joins', () => {
const val = { latestDescriptions: [] };
expect(EcastShardClient.parsePlayerJoinFromTextDescriptions(val)).toEqual([]);
});
test('handles null/undefined val', () => {
expect(EcastShardClient.parsePlayerJoinFromTextDescriptions(null)).toEqual([]);
expect(EcastShardClient.parsePlayerJoinFromTextDescriptions(undefined)).toEqual([]);
});
});
describe('constructor', () => {
test('initializes with correct defaults', () => {
const client = new EcastShardClient({
sessionId: 1, gameId: 5, roomCode: 'TEST', maxPlayers: 8, onEvent: () => {}
});
expect(client.sessionId).toBe(1);
expect(client.gameId).toBe(5);
expect(client.roomCode).toBe('TEST');
expect(client.maxPlayers).toBe(8);
expect(client.playerCount).toBe(0);
expect(client.playerNames).toEqual([]);
expect(client.gameStarted).toBe(false);
expect(client.gameFinished).toBe(false);
expect(client.ws).toBeNull();
});
});
describe('handleWelcome', () => {
test('parses welcome message and sets internal state', () => {
const client = new EcastShardClient({
sessionId: 1, gameId: 5, roomCode: 'TEST', maxPlayers: 8, onEvent: () => {}
});
client.handleWelcome({
id: 7,
secret: 'abc-123',
reconnect: false,
entities: {
room: ['object', { key: 'room', val: { state: 'Lobby', lobbyState: 'CanStart', gameCanStart: true, gameIsStarting: false, gameFinished: false }, version: 0, from: 1 }, { locked: false }]
},
here: {
'1': { id: 1, roles: { host: {} } },
'2': { id: 2, roles: { player: { name: 'Alice' } } },
'3': { id: 3, roles: { player: { name: 'Bob' } } },
}
});
expect(client.shardId).toBe(7);
expect(client.secret).toBe('abc-123');
expect(client.playerCount).toBe(2);
expect(client.playerNames).toEqual(['Alice', 'Bob']);
expect(client.gameState).toBe('Lobby');
expect(client.lobbyState).toBe('CanStart');
expect(client.gameStarted).toBe(false);
});
});
describe('handleEntityUpdate', () => {
test('updates room state on room entity update', () => {
const client = new EcastShardClient({
sessionId: 1, gameId: 5, roomCode: 'TEST', maxPlayers: 8, onEvent: () => {}
});
client.gameState = 'Lobby';
client.lobbyState = 'WaitingForMore';
client.handleEntityUpdate({
key: 'room',
val: { state: 'Gameplay', lobbyState: 'Countdown', gameCanStart: true, gameIsStarting: true, gameFinished: false },
version: 5,
from: 1
});
expect(client.gameState).toBe('Gameplay');
expect(client.gameStarted).toBe(true);
});
test('handles bc:room key as room update', () => {
const client = new EcastShardClient({
sessionId: 1, gameId: 5, roomCode: 'TEST', maxPlayers: 8, onEvent: () => {}
});
client.handleEntityUpdate({
key: 'bc:room',
val: { state: 'Lobby', lobbyState: 'CanStart', gameCanStart: true, gameIsStarting: false, gameFinished: false },
version: 1,
from: 1
});
expect(client.lobbyState).toBe('CanStart');
});
});
});